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
;
89 static unsigned char vo_osd_text
[64];
94 * this function is called by aa lib if windows resizes
95 * further during init, because here we have to calculate
101 aspect_save_screenres(aa_imgwidth(c
),aa_imgheight(c
));
102 image_height
= aa_imgheight(c
); //src_height;
103 image_width
= aa_imgwidth(c
); //src_width;
105 aspect(&image_width
,&image_height
,A_ZOOM
);
107 image_x
= (aa_imgwidth(c
) - image_width
) / 2;
108 image_y
= (aa_imgheight(c
) - image_height
) / 2;
109 screen_w
= image_width
* aa_scrwidth(c
) / aa_imgwidth(c
);
110 screen_h
= image_height
* aa_scrheight(c
) / aa_imgheight(c
);
111 screen_x
= (aa_scrwidth(c
) - screen_w
) / 2;
112 screen_y
= (aa_scrheight(c
) - screen_h
) / 2;
114 if(sws
) sws_freeContext(sws
);
115 sws
= sws_getContextFromCmdLine(src_width
,src_height
,image_format
,
116 image_width
,image_height
,IMGFMT_Y8
);
118 image
[0] = aa_image(c
) + image_y
* aa_imgwidth(c
) + image_x
;
122 image_stride
[0] = aa_imgwidth(c
);
131 osdmessage(int duration
, int deko
, const char *fmt
, ...)
134 * for outputting a centered string at the bottom
135 * of our window for a while
138 char m
[MESSAGE_SIZE
];
139 unsigned int old_len
= strlen(osdmessagetext
);
142 vsprintf(m
, fmt
, ar
);
144 if (deko
==1) sprintf(osdmessagetext
, MESSAGE_DEKO
, m
);
145 else strcpy(osdmessagetext
, m
);
147 if(old_len
> strlen(osdmessagetext
)) {
148 memset(c
->textbuffer
+ osdy
* aa_scrwidth(c
) + osdx
,' ',old_len
);
149 memset(c
->attrbuffer
+ osdy
* aa_scrwidth(c
) + osdx
,0,old_len
);
152 stoposd
= time(NULL
) + duration
;
153 osdx
=(aa_scrwidth(c
) / 2) - (strlen(osdmessagetext
) / 2 ) ;
158 osdpercent(int duration
, int deko
, int min
, int max
, int val
, const char * desc
, const char * unit
)
161 * prints a bar for setting values
168 step
=(float)aa_scrwidth(c
) /(float)(max
-min
);
169 where
=(val
-min
)*step
;
170 osdmessage(duration
,deko
,"%s: %i%s",desc
, val
, unit
);
172 posbar
[aa_scrwidth(c
)-1]='|';
173 for (i
=0;i
<aa_scrwidth(c
);i
++){
174 if (i
==where
) posbar
[i
]='#';
177 if (where
!=0) posbar
[0]='|';
178 if (where
!=(aa_scrwidth(c
)-1) ) posbar
[aa_scrwidth(c
)-1]='|';
180 posbar
[aa_scrwidth(c
)]='\0';
188 * places the mplayer status osd
190 if (vo_osd_text
[0] != 0) {
192 if(vo_osd_text
[0] < 32) {
193 len
= strlen(sub_osd_names_short
[vo_osd_text
[0]]) + strlen(vo_osd_text
+1) + 2;
194 aa_printf(c
, 0, 0 , aaopt_osdcolor
, "%s %s ", sub_osd_names_short
[vo_osd_text
[0]], vo_osd_text
+1);
196 len
= strlen(vo_osd_text
) + 1;
197 aa_printf(c
, 0, 0 , aaopt_osdcolor
, "%s ",vo_osd_text
);
200 if(len
< osd_text_length
) {
201 memset(c
->textbuffer
+ len
,' ',osd_text_length
- len
);
202 memset(c
->attrbuffer
+ len
,0,osd_text_length
- len
);
204 osd_text_length
= len
;
210 printosdprogbar(void){
211 /* print mplayer osd-progbar */
212 if (vo_osd_progbar_type
!=-1){
213 osdpercent(1,1,0,255,vo_osd_progbar_value
, sub_osd_names
[vo_osd_progbar_type
], "");
217 config(uint32_t width
, uint32_t height
, uint32_t d_width
,
218 uint32_t d_height
, uint32_t flags
, char *title
,
227 aspect_save_orig(width
,height
);
228 aspect_save_prescale(d_width
,d_height
);
232 image_format
= format
;
234 /* nothing will change its size, be we need some values initialized */
237 /* now init out own 'font' (to use vo_draw_text_sub without edit them) */
238 if(!vo_font_save
) vo_font_save
= vo_font
;
239 if(vo_font
== vo_font_save
) {
240 vo_font
=malloc(sizeof(font_desc_t
));//if(!desc) return NULL;
241 memset(vo_font
,0,sizeof(font_desc_t
));
242 vo_font
->pic_a
[0]=malloc(sizeof(raw_file
));
243 memset(vo_font
->pic_a
[0],0,sizeof(raw_file
));
244 vo_font
->pic_b
[0]=malloc(sizeof(raw_file
));
245 memset(vo_font
->pic_b
[0],0,sizeof(raw_file
));
247 #ifdef CONFIG_FREETYPE
248 vo_font
->dynamic
= 0;
251 vo_font
->spacewidth
=1;
252 vo_font
->charspace
=0;
254 vo_font
->pic_a
[0]->bmp
=malloc(255);
255 vo_font
->pic_a
[0]->pal
=NULL
;
256 vo_font
->pic_b
[0]->bmp
=malloc(255);
257 vo_font
->pic_b
[0]->pal
=NULL
;
258 vo_font
->pic_a
[0]->w
=1;
259 vo_font
->pic_a
[0]->h
=1;
260 for (i
=0; i
<255; i
++){
264 vo_font
->pic_a
[0]->bmp
[i
]=i
;
265 vo_font
->pic_b
[0]->bmp
[i
]=i
;
270 osdmessage(5, 1, "Welcome to ASCII ART MPlayer");
272 mp_msg(MSGT_VO
,MSGL_V
,"VO: [aa] screendriver: %s\n", c
->driver
->name
);
273 mp_msg(MSGT_VO
,MSGL_V
,"VO: [aa] keyboarddriver: %s\n", c
->kbddriver
->name
);
275 mp_msg(MSGT_VO
,MSGL_INFO
,
277 "Important suboptions\n"
278 "\textended use use all 256 characters\n"
279 "\teight use eight bit ascii\n"
280 "\tdriver set recommended aalib driver (X11,curses,linux)\n"
281 "\thelp to see all options provided by aalib\n"
286 "\t3 : brightness -\n"
287 "\t4 : brightness +\n"
288 "\t5 : fast rendering\n"
290 "\t7 : invert image\n"
291 "\ta : toggles between aa and mplayer control\n"
294 "All other keys are MPlayer defaults.\n"
303 query_format(uint32_t format
) {
305 * ...are we able to... ?
307 * All input format supported by the sws
322 return VFCAP_CSP_SUPPORTED
| VFCAP_SWSCALE
| VFCAP_OSD
;
328 draw_frame(uint8_t *src
[]) {
329 int stride
[3] = { 0 , 0 , 0 };
331 switch(image_format
) {
334 stride
[0] = src_width
*2;
338 stride
[0] = src_width
*3;
341 stride
[0] = src_width
*4;
345 sws_scale_ordered(sws
,src
,stride
,0,src_height
,image
,image_stride
);
347 /* Now 'ASCIInate' the image */
349 aa_fastrender(c
, screen_x
, screen_y
, screen_w
+ screen_x
, screen_h
+ screen_y
);
351 aa_render(c
, p
,screen_x
, screen_y
, screen_w
+ screen_x
, screen_h
+ screen_y
);
357 draw_slice(uint8_t *src
[], int stride
[],
358 int w
, int h
, int x
, int y
) {
360 int dx1
= screen_x
+ (x
* screen_w
/ src_width
);
361 int dy1
= screen_y
+ (y
* screen_h
/ src_height
);
362 int dx2
= screen_x
+ ((x
+w
) * screen_w
/ src_width
);
363 int dy2
= screen_y
+ ((y
+h
) * screen_h
/ src_height
);
365 sws_scale_ordered(sws
,src
,stride
,y
,h
,image
,image_stride
);
367 /* Now 'ASCIInate' the image */
369 aa_fastrender(c
, dx1
, dy1
, dx2
, dy2
);
371 aa_render(c
, p
,dx1
, dy1
, dx2
, dy2
);
380 /* do we have to put *our* (messages, progbar) osd to aa's txtbuf ? */
383 if (time(NULL
)>=stoposd
) {
385 if(*osdmessagetext
) {
386 memset(c
->textbuffer
+ osdy
* aa_scrwidth(c
) + osdx
,' ',strlen(osdmessagetext
));
387 memset(c
->attrbuffer
+ osdy
* aa_scrwidth(c
) + osdx
,0,strlen(osdmessagetext
));
388 osdmessagetext
[0] = '\0';
391 memset(c
->textbuffer
+ (osdy
+1) * aa_scrwidth(c
),' ',strlen(posbar
));
392 memset(c
->attrbuffer
+ (osdy
+1) * aa_scrwidth(c
),0,strlen(posbar
));
396 aa_puts(c
, osdx
, osdy
, AA_SPECIAL
, osdmessagetext
);
399 aa_puts(c
, 0, osdy
+ 1, AA_SPECIAL
, posbar
);
402 /* OSD time & playmode , subtitles */
414 * called by show_image and mplayer
417 while ((key
=aa_getevent(c
,0))!=AA_NONE
){
419 /* some conversations */
422 mplayer_put_key(KEY_UP
);
425 mplayer_put_key(KEY_DOWN
);
428 mplayer_put_key(KEY_LEFT
);
431 mplayer_put_key(KEY_RIGHT
);
434 mplayer_put_key(KEY_ESC
);
437 mplayer_put_key(KEY_PAGE_UP
);
440 mplayer_put_key(KEY_PAGE_DOWN
);
443 continue; /* aa lib special key */
447 if (key
=='a' || key
=='A'){
448 aaconfigmode
=!aaconfigmode
;
449 osdmessage(MESSAGE_DURATION
, 1, "aa config mode is now %s",
450 aaconfigmode
==1 ? "on. use keys 5-7" : "off");
452 if (aaconfigmode
==1) {
454 /* AA image controls */
457 osdmessage(MESSAGE_DURATION
, 1, "Fast mode is now %s", fast
==1 ? "on" : "off");
460 if (p
->dither
==AA_FLOYD_S
){
462 osdmessage(MESSAGE_DURATION
, 1, "Dithering: Off");
463 }else if (p
->dither
==AA_NONE
){
464 p
->dither
=AA_ERRORDISTRIB
;
465 osdmessage(MESSAGE_DURATION
, 1, "Dithering: Error Distribution");
466 }else if (p
->dither
==AA_ERRORDISTRIB
){
467 p
->dither
=AA_FLOYD_S
;
468 osdmessage(MESSAGE_DURATION
, 1, "Dithering: Floyd Steinberg");
472 p
->inversion
=!p
->inversion
;
473 osdmessage(MESSAGE_DURATION
, 1, "Invert mode is now %s",
474 p
->inversion
==1 ? "on" : "off");
478 /* nothing if we're interested in?
479 * the mplayer should handle it!
481 mplayer_put_key(key
);
485 else mplayer_put_key(key
);
495 if (strstr(c
->driver
->name
,"Curses") || strstr(c
->driver
->name
,"Linux")){
496 freopen("/dev/tty", "w", stderr
);
499 free(vo_font
->pic_a
[0]->bmp
);
500 free(vo_font
->pic_a
[0]);
501 free(vo_font
->pic_b
[0]->bmp
);
502 free(vo_font
->pic_b
[0]);
504 vo_font
= vo_font_save
;
510 static void draw_alpha(int x
,int y
, int w
,int h
, unsigned char* src
, unsigned char *srca
, int stride
){
512 for (i
= 0; i
< h
; i
++) {
513 for (j
= 0; j
< w
; j
++) {
514 if (src
[i
*stride
+j
] > 0) {
515 c
->textbuffer
[x
+ j
+ (y
+i
)*aa_scrwidth(c
)] = src
[i
*stride
+j
];
516 c
->attrbuffer
[x
+ j
+ (y
+i
)*aa_scrwidth(c
)] = aaopt_subcolor
;
522 static void clear_alpha(int x0
,int y0
, int w
,int h
) {
525 for(l
= 0 ; l
< h
; l
++) {
526 memset(c
->textbuffer
+ (y0
+ l
) * aa_scrwidth(c
) + x0
,' ',w
);
527 memset(c
->attrbuffer
+ (y0
+ l
) * aa_scrwidth(c
) + x0
,0,w
);
534 char vo_osd_text_save
;
535 int vo_osd_progbar_type_save
;
538 /* let vo_draw_text only write subtitle */
539 vo_osd_text_save
= global_osd
->osd_text
[0];
540 global_osd
->osd_text
[0] = 0;
541 vo_osd_progbar_type_save
=vo_osd_progbar_type
;
542 vo_osd_progbar_type
=-1;
543 vo_remove_text(aa_scrwidth(c
), aa_scrheight(c
),clear_alpha
);
544 vo_draw_text(aa_scrwidth(c
), aa_scrheight(c
), draw_alpha
);
545 global_osd
->osd_text
[0] = vo_osd_text_save
;
546 vo_osd_progbar_type
=vo_osd_progbar_type_save
;
553 if (s
==NULL
) return -1;
554 i
=strtol(s
, &rest
, 10);
555 if ((rest
==NULL
|| strlen(rest
)==0) && i
>=0 && i
<=5) return i
;
556 if (!strcasecmp(s
, "normal")) return AA_NORMAL
;
557 else if (!strcasecmp(s
, "dim")) return AA_DIM
;
558 else if (!strcasecmp(s
, "bold")) return AA_BOLD
;
559 else if (!strcasecmp(s
, "boldfont")) return AA_BOLDFONT
;
560 else if (!strcasecmp(s
, "special")) return AA_SPECIAL
;
564 static int parse_suboptions(const char *arg
) {
565 char *pseudoargv
[4], *osdcolor
= NULL
, *subcolor
= NULL
, **strings
,
567 int pseudoargc
, displayhelp
= 0, *booleans
;
568 opt_t extra_opts
[] = {
569 {"osdcolor", OPT_ARG_MSTRZ
, &osdcolor
, NULL
, 0},
570 {"subcolor", OPT_ARG_MSTRZ
, &subcolor
, NULL
, 0},
571 {"help", OPT_ARG_BOOL
, &displayhelp
, NULL
, 0} };
572 opt_t
*subopts
= NULL
, *p
;
573 char *strings_list
[] = {"-driver", "-kbddriver", "-mousedriver", "-font",
574 "-width", "-height", "-minwidth", "-minheight", "-maxwidth",
575 "-maxheight", "-recwidth", "-recheight", "-bright", "-contrast",
576 "-gamma", "-dimmul", "-boldmul", "-random" };
577 char *booleans_list
[] = {"-dim", "-bold", "-reverse", "-normal",
578 "-boldfont", "-inverse", "-extended", "-eight", "-dither",
579 "-floyd_steinberg", "-error_distribution"};
580 char *nobooleans_list
[] = {"-nodim", "-nobold", "-noreverse", "-nonormal",
581 "-noboldfont", "-noinverse", "-noextended", "-noeight", "-nodither",
582 "-nofloyd_steinberg", "-noerror_distribution"};
583 const int nstrings
= sizeof(strings_list
) / sizeof(char*);
584 const int nbooleans
= sizeof(booleans_list
) / sizeof(char*);
585 const int nextra_opts
= sizeof(extra_opts
) / sizeof(opt_t
);
586 const int nsubopts
= nstrings
+ nbooleans
+ nextra_opts
;
589 subopts
= calloc(nsubopts
+ 1, sizeof(opt_t
));
590 strings
= calloc(nstrings
, sizeof(char*));
591 booleans
= calloc(nbooleans
, sizeof(int));
594 for (i
=0; i
<nstrings
; i
++, p
++) {
595 p
->name
= strings_list
[i
] + 1; // skip '-'
596 p
->type
= OPT_ARG_MSTRZ
;
597 p
->valp
= &strings
[i
];
599 for (i
=0; i
<nbooleans
; i
++, p
++) {
600 p
->name
= booleans_list
[i
] + 1;
601 p
->type
= OPT_ARG_BOOL
;
602 p
->valp
= &booleans
[i
];
604 memcpy(p
, extra_opts
, sizeof(extra_opts
));
606 retval
= subopt_parse(arg
, subopts
);
608 if (retval
== 0 && displayhelp
) {
609 helpmsg
= strdup(aa_help
);
610 for (i
=0; i
<(signed)strlen(helpmsg
); i
++)
611 if (helpmsg
[i
] == '-') helpmsg
[i
] = ' ';
612 mp_msg(MSGT_VO
, MSGL_INFO
, MSGTR_VO_AA_HelpHeader
);
613 mp_msg(MSGT_VO
, MSGL_INFO
, "%s\n\n", helpmsg
);
614 mp_msg(MSGT_VO
, MSGL_INFO
, MSGTR_VO_AA_AdditionalOptions
);
618 pseudoargv
[3] = NULL
;
619 for (i
=0; i
<nstrings
; i
++) {
620 pseudoargc
= 3; // inside loop because aalib changes it
621 if (strings
[i
] != NULL
) {
622 pseudoargv
[1] = strings_list
[i
];
623 pseudoargv
[2] = strings
[i
];
624 aa_parseoptions(&aa_defparams
, &aa_defrenderparams
,
625 &pseudoargc
, pseudoargv
);
628 pseudoargv
[2] = NULL
;
629 for (i
=0; i
<nbooleans
; i
++) {
631 if (booleans
[i
]) pseudoargv
[1] = booleans_list
[i
];
632 else pseudoargv
[1] = nobooleans_list
[i
];
633 aa_parseoptions(&aa_defparams
, &aa_defrenderparams
,
634 &pseudoargc
, pseudoargv
);
636 if (osdcolor
) aaopt_osdcolor
= getcolor(osdcolor
);
637 if (subcolor
) aaopt_subcolor
= getcolor(subcolor
);
640 if (subopts
) free(subopts
);
641 if (booleans
) free(booleans
);
643 for (i
=0; i
<nstrings
; i
++)
648 if (osdcolor
) free(osdcolor
);
649 if (subcolor
) free(subcolor
);
650 if (helpmsg
) free(helpmsg
);
654 static int preinit(const char *arg
)
658 int fd
, vt
, major
, minor
;
664 if (parse_suboptions(arg
) != 0)
668 /* initializing of aalib */
670 hidis
=aa_getfirst(&aa_displayrecommended
);
672 /* check /dev/vcsa<vt> */
673 /* check only, if no driver is explicit set */
674 fd
= dup (fileno (stderr
));
676 major
= sbuf
.st_rdev
>> 8;
677 vt
= minor
= sbuf
.st_rdev
& 0xff;
679 sprintf (fname
, "/dev/vcsa%2.2i", vt
);
680 fp
= fopen (fname
, "w+");
682 fprintf(stderr
,"VO: [aa] cannot open %s for writing,"
683 "so we'll not use linux driver\n", fname
);
684 aa_recommendlowdisplay("linux");
685 aa_recommendhidisplay("curses");
686 aa_recommendhidisplay("X11");
688 } else aa_recommendhidisplay(hidis
);
689 c
= aa_autoinit(&aa_defparams
);
692 mp_msg(MSGT_VO
,MSGL_ERR
,"Cannot initialize aalib\n");
695 if (!aa_autoinitkbd(c
,0)) {
696 mp_msg(MSGT_VO
,MSGL_ERR
,"Cannot initialize keyboard\n");
701 aa_resizehandler(c
, (void *)resize
);
703 p
= aa_getrenderparams();
705 if ((strstr(c
->driver
->name
,"Curses")) || (strstr(c
->driver
->name
,"Linux"))){
706 freopen("/dev/null", "w", stderr
);
707 /* disable console blanking */
711 memset(image
,0,3*sizeof(uint8_t));
712 osdmessagetext
[0] = '\0';
718 static int control(uint32_t request
, void *data
)
721 case VOCTRL_QUERY_FORMAT
:
722 return query_format(*((uint32_t*)data
));
723 case VOCTRL_SET_EQUALIZER
: {
724 struct voctrl_set_equalizer_args
*args
= data
;
725 if (strcmp(args
->name
, "contrast") == 0)
726 p
->contrast
= (args
->value
+ 100) * 64 / 100;
727 else if (strcmp(args
->name
, "brightness") == 0)
728 p
->bright
= (args
->value
+ 100) * 128 / 100;
731 case VOCTRL_GET_EQUALIZER
: {
732 struct voctrl_get_equalizer_args
*args
= data
;
734 if (strcmp(args
->name
, "contrast") == 0)
735 *args
->valueptr
= (p
->contrast
- 64) * 100 / 64;
736 else if (strcmp(args
->name
, "brightness") == 0)
737 *args
->valueptr
= (p
->bright
- 128) * 100 / 128;