2 * video output driver for AAlib
4 * copyright (c) 2001 Folke Ashberg <folke@ashberg.de>
6 * This file is part of MPlayer.
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 of the License, or
11 * (at your option) any later version.
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.
37 #include "video_out.h"
38 #include "video_out_internal.h"
40 #include "libswscale/swscale.h"
41 #include "libmpcodecs/vf_scale.h"
42 #include "font_load.h"
45 #include "osdep/keycodes.h"
47 #include "subopt-helper.h"
53 #define MESSAGE_DURATION 3
54 #define MESSAGE_SIZE 512
55 #define MESSAGE_DEKO " +++ %s +++ "
57 static const vo_info_t info
= {
60 "Alban Bedel <albeu@free.fr> and Folke Ashberg <folke@ashberg.de>",
64 const LIBVO_EXTERN(aa
)
66 /* aa's main context we use */
70 /* used for the sws */
71 static uint8_t * image
[3];
72 static int image_stride
[3];
75 static int image_format
;
76 static int image_width
;
77 static int image_height
;
78 static int image_x
, image_y
;
79 static int screen_x
, screen_y
;
80 static int screen_w
, screen_h
;
82 static int src_height
;
86 static int showosdmessage
= 0;
87 char osdmessagetext
[MESSAGE_SIZE
];
88 char posbar
[MESSAGE_SIZE
];
89 static int osdx
, osdy
;
90 static int osd_text_length
= 0;
92 font_desc_t
* vo_font_save
= NULL
;
93 static struct SwsContext
*sws
=NULL
;
96 int aaopt_osdcolor
= AA_SPECIAL
;
97 int aaopt_subcolor
= AA_SPECIAL
;
99 static unsigned char vo_osd_text
[64];
104 * this function is called by aa lib if windows resizes
105 * further during init, because here we have to calculate
111 aspect_save_screenres(aa_imgwidth(c
),aa_imgheight(c
));
112 image_height
= aa_imgheight(c
); //src_height;
113 image_width
= aa_imgwidth(c
); //src_width;
115 aspect(&image_width
,&image_height
,A_ZOOM
);
117 image_x
= (aa_imgwidth(c
) - image_width
) / 2;
118 image_y
= (aa_imgheight(c
) - image_height
) / 2;
119 screen_w
= image_width
* aa_scrwidth(c
) / aa_imgwidth(c
);
120 screen_h
= image_height
* aa_scrheight(c
) / aa_imgheight(c
);
121 screen_x
= (aa_scrwidth(c
) - screen_w
) / 2;
122 screen_y
= (aa_scrheight(c
) - screen_h
) / 2;
124 if(sws
) sws_freeContext(sws
);
125 sws
= sws_getContextFromCmdLine(src_width
,src_height
,image_format
,
126 image_width
,image_height
,IMGFMT_Y8
);
128 image
[0] = aa_image(c
) + image_y
* aa_imgwidth(c
) + image_x
;
132 image_stride
[0] = aa_imgwidth(c
);
141 osdmessage(int duration
, int deko
, const char *fmt
, ...)
144 * for outputting a centered string at the bottom
145 * of our window for a while
148 char m
[MESSAGE_SIZE
];
149 unsigned int old_len
= strlen(osdmessagetext
);
152 vsprintf(m
, fmt
, ar
);
154 if (deko
==1) sprintf(osdmessagetext
, MESSAGE_DEKO
, m
);
155 else strcpy(osdmessagetext
, m
);
157 if(old_len
> strlen(osdmessagetext
)) {
158 memset(c
->textbuffer
+ osdy
* aa_scrwidth(c
) + osdx
,' ',old_len
);
159 memset(c
->attrbuffer
+ osdy
* aa_scrwidth(c
) + osdx
,0,old_len
);
162 stoposd
= time(NULL
) + duration
;
163 osdx
=(aa_scrwidth(c
) / 2) - (strlen(osdmessagetext
) / 2 ) ;
168 osdpercent(int duration
, int deko
, int min
, int max
, int val
, const char * desc
, const char * unit
)
171 * prints a bar for setting values
178 step
=(float)aa_scrwidth(c
) /(float)(max
-min
);
179 where
=(val
-min
)*step
;
180 osdmessage(duration
,deko
,"%s: %i%s",desc
, val
, unit
);
182 posbar
[aa_scrwidth(c
)-1]='|';
183 for (i
=0;i
<aa_scrwidth(c
);i
++){
184 if (i
==where
) posbar
[i
]='#';
187 if (where
!=0) posbar
[0]='|';
188 if (where
!=(aa_scrwidth(c
)-1) ) posbar
[aa_scrwidth(c
)-1]='|';
190 posbar
[aa_scrwidth(c
)]='\0';
198 * places the mplayer status osd
200 if (vo_osd_text
[0] != 0) {
202 if(vo_osd_text
[0] < 32) {
203 len
= strlen(sub_osd_names_short
[vo_osd_text
[0]]) + strlen(vo_osd_text
+1) + 2;
204 aa_printf(c
, 0, 0 , aaopt_osdcolor
, "%s %s ", sub_osd_names_short
[vo_osd_text
[0]], vo_osd_text
+1);
206 len
= strlen(vo_osd_text
) + 1;
207 aa_printf(c
, 0, 0 , aaopt_osdcolor
, "%s ",vo_osd_text
);
210 if(len
< osd_text_length
) {
211 memset(c
->textbuffer
+ len
,' ',osd_text_length
- len
);
212 memset(c
->attrbuffer
+ len
,0,osd_text_length
- len
);
214 osd_text_length
= len
;
220 printosdprogbar(void){
221 /* print mplayer osd-progbar */
222 if (vo_osd_progbar_type
!=-1){
223 osdpercent(1,1,0,255,vo_osd_progbar_value
, sub_osd_names
[vo_osd_progbar_type
], "");
227 config(uint32_t width
, uint32_t height
, uint32_t d_width
,
228 uint32_t d_height
, uint32_t flags
, char *title
,
237 aspect_save_orig(width
,height
);
238 aspect_save_prescale(d_width
,d_height
);
242 image_format
= format
;
244 /* nothing will change its size, be we need some values initialized */
247 /* now init our own 'font' */
248 if(!vo_font_save
) vo_font_save
= vo_font
;
249 if(vo_font
== vo_font_save
) {
250 vo_font
=malloc(sizeof(font_desc_t
));//if(!desc) return NULL;
251 memset(vo_font
,0,sizeof(font_desc_t
));
252 vo_font
->pic_a
[0]=malloc(sizeof(raw_file
));
253 memset(vo_font
->pic_a
[0],0,sizeof(raw_file
));
254 vo_font
->pic_b
[0]=malloc(sizeof(raw_file
));
255 memset(vo_font
->pic_b
[0],0,sizeof(raw_file
));
257 #ifdef CONFIG_FREETYPE
258 vo_font
->dynamic
= 0;
261 vo_font
->spacewidth
=1;
262 vo_font
->charspace
=0;
264 vo_font
->pic_a
[0]->bmp
=malloc(255);
265 vo_font
->pic_a
[0]->pal
=NULL
;
266 vo_font
->pic_b
[0]->bmp
=malloc(255);
267 vo_font
->pic_b
[0]->pal
=NULL
;
268 vo_font
->pic_a
[0]->w
=1;
269 vo_font
->pic_a
[0]->h
=1;
270 for (i
=0; i
<255; i
++){
274 vo_font
->pic_a
[0]->bmp
[i
]=i
;
275 vo_font
->pic_b
[0]->bmp
[i
]=i
;
280 osdmessage(5, 1, "Welcome to ASCII ART MPlayer");
282 mp_msg(MSGT_VO
,MSGL_V
,"VO: [aa] screendriver: %s\n", c
->driver
->name
);
283 mp_msg(MSGT_VO
,MSGL_V
,"VO: [aa] keyboarddriver: %s\n", c
->kbddriver
->name
);
285 mp_msg(MSGT_VO
,MSGL_INFO
,
287 "Important suboptions\n"
288 "\textended use use all 256 characters\n"
289 "\teight use eight bit ascii\n"
290 "\tdriver set recommended aalib driver (X11,curses,linux)\n"
291 "\thelp to see all options provided by aalib\n"
296 "\t3 : brightness -\n"
297 "\t4 : brightness +\n"
298 "\t5 : fast rendering\n"
300 "\t7 : invert image\n"
301 "\ta : toggles between aa and mplayer control\n"
304 "All other keys are MPlayer defaults.\n"
313 query_format(uint32_t format
) {
315 * ...are we able to... ?
317 * All input format supported by the sws
332 return VFCAP_CSP_SUPPORTED
| VFCAP_SWSCALE
| VFCAP_OSD
;
338 draw_frame(uint8_t *src
[]) {
339 int stride
[3] = { 0 , 0 , 0 };
341 switch(image_format
) {
344 stride
[0] = src_width
*2;
348 stride
[0] = src_width
*3;
351 stride
[0] = src_width
*4;
355 sws_scale_ordered(sws
,src
,stride
,0,src_height
,image
,image_stride
);
357 /* Now 'ASCIInate' the image */
359 aa_fastrender(c
, screen_x
, screen_y
, screen_w
+ screen_x
, screen_h
+ screen_y
);
361 aa_render(c
, p
,screen_x
, screen_y
, screen_w
+ screen_x
, screen_h
+ screen_y
);
367 draw_slice(uint8_t *src
[], int stride
[],
368 int w
, int h
, int x
, int y
) {
370 int dx1
= screen_x
+ (x
* screen_w
/ src_width
);
371 int dy1
= screen_y
+ (y
* screen_h
/ src_height
);
372 int dx2
= screen_x
+ ((x
+w
) * screen_w
/ src_width
);
373 int dy2
= screen_y
+ ((y
+h
) * screen_h
/ src_height
);
375 sws_scale_ordered(sws
,src
,stride
,y
,h
,image
,image_stride
);
377 /* Now 'ASCIInate' the image */
379 aa_fastrender(c
, dx1
, dy1
, dx2
, dy2
);
381 aa_render(c
, p
,dx1
, dy1
, dx2
, dy2
);
390 /* do we have to put *our* (messages, progbar) osd to aa's txtbuf ? */
393 if (time(NULL
)>=stoposd
) {
395 if(*osdmessagetext
) {
396 memset(c
->textbuffer
+ osdy
* aa_scrwidth(c
) + osdx
,' ',strlen(osdmessagetext
));
397 memset(c
->attrbuffer
+ osdy
* aa_scrwidth(c
) + osdx
,0,strlen(osdmessagetext
));
398 osdmessagetext
[0] = '\0';
401 memset(c
->textbuffer
+ (osdy
+1) * aa_scrwidth(c
),' ',strlen(posbar
));
402 memset(c
->attrbuffer
+ (osdy
+1) * aa_scrwidth(c
),0,strlen(posbar
));
406 aa_puts(c
, osdx
, osdy
, AA_SPECIAL
, osdmessagetext
);
409 aa_puts(c
, 0, osdy
+ 1, AA_SPECIAL
, posbar
);
412 /* OSD time & playmode , subtitles */
424 * called by show_image and mplayer
427 while ((key
=aa_getevent(c
,0))!=AA_NONE
){
429 /* some conversations */
432 mplayer_put_key(KEY_UP
);
435 mplayer_put_key(KEY_DOWN
);
438 mplayer_put_key(KEY_LEFT
);
441 mplayer_put_key(KEY_RIGHT
);
444 mplayer_put_key(KEY_ESC
);
447 mplayer_put_key(KEY_PAGE_UP
);
450 mplayer_put_key(KEY_PAGE_DOWN
);
453 continue; /* aa lib special key */
457 if (key
=='a' || key
=='A'){
458 aaconfigmode
=!aaconfigmode
;
459 osdmessage(MESSAGE_DURATION
, 1, "aa config mode is now %s",
460 aaconfigmode
==1 ? "on. use keys 5-7" : "off");
462 if (aaconfigmode
==1) {
464 /* AA image controls */
467 osdmessage(MESSAGE_DURATION
, 1, "Fast mode is now %s", fast
==1 ? "on" : "off");
470 if (p
->dither
==AA_FLOYD_S
){
472 osdmessage(MESSAGE_DURATION
, 1, "Dithering: Off");
473 }else if (p
->dither
==AA_NONE
){
474 p
->dither
=AA_ERRORDISTRIB
;
475 osdmessage(MESSAGE_DURATION
, 1, "Dithering: Error Distribution");
476 }else if (p
->dither
==AA_ERRORDISTRIB
){
477 p
->dither
=AA_FLOYD_S
;
478 osdmessage(MESSAGE_DURATION
, 1, "Dithering: Floyd Steinberg");
482 p
->inversion
=!p
->inversion
;
483 osdmessage(MESSAGE_DURATION
, 1, "Invert mode is now %s",
484 p
->inversion
==1 ? "on" : "off");
488 /* nothing if we're interested in?
489 * the mplayer should handle it!
491 mplayer_put_key(key
);
495 else mplayer_put_key(key
);
505 if (strstr(c
->driver
->name
,"Curses") || strstr(c
->driver
->name
,"Linux")){
506 freopen("/dev/tty", "w", stderr
);
509 free(vo_font
->pic_a
[0]->bmp
);
510 free(vo_font
->pic_a
[0]);
511 free(vo_font
->pic_b
[0]->bmp
);
512 free(vo_font
->pic_b
[0]);
514 vo_font
= vo_font_save
;
520 static void draw_alpha(int x
,int y
, int w
,int h
, unsigned char* src
, unsigned char *srca
, int stride
){
522 for (i
= 0; i
< h
; i
++) {
523 for (j
= 0; j
< w
; j
++) {
524 if (src
[i
*stride
+j
] > 0) {
525 c
->textbuffer
[x
+ j
+ (y
+i
)*aa_scrwidth(c
)] = src
[i
*stride
+j
];
526 c
->attrbuffer
[x
+ j
+ (y
+i
)*aa_scrwidth(c
)] = aaopt_subcolor
;
532 static void clear_alpha(int x0
,int y0
, int w
,int h
) {
535 for(l
= 0 ; l
< h
; l
++) {
536 memset(c
->textbuffer
+ (y0
+ l
) * aa_scrwidth(c
) + x0
,' ',w
);
537 memset(c
->attrbuffer
+ (y0
+ l
) * aa_scrwidth(c
) + x0
,0,w
);
544 char vo_osd_text_save
;
545 int vo_osd_progbar_type_save
;
548 /* let vo_draw_text only write subtitle */
549 vo_osd_text_save
= global_osd
->osd_text
[0];
550 global_osd
->osd_text
[0] = 0;
551 vo_osd_progbar_type_save
=vo_osd_progbar_type
;
552 vo_osd_progbar_type
=-1;
553 vo_remove_text(aa_scrwidth(c
), aa_scrheight(c
),clear_alpha
);
554 vo_draw_text(aa_scrwidth(c
), aa_scrheight(c
), draw_alpha
);
555 global_osd
->osd_text
[0] = vo_osd_text_save
;
556 vo_osd_progbar_type
=vo_osd_progbar_type_save
;
563 if (s
==NULL
) return -1;
564 i
=strtol(s
, &rest
, 10);
565 if ((rest
==NULL
|| strlen(rest
)==0) && i
>=0 && i
<=5) return i
;
566 if (!strcasecmp(s
, "normal")) return AA_NORMAL
;
567 else if (!strcasecmp(s
, "dim")) return AA_DIM
;
568 else if (!strcasecmp(s
, "bold")) return AA_BOLD
;
569 else if (!strcasecmp(s
, "boldfont")) return AA_BOLDFONT
;
570 else if (!strcasecmp(s
, "special")) return AA_SPECIAL
;
574 static int parse_suboptions(const char *arg
) {
575 char *pseudoargv
[4], *osdcolor
= NULL
, *subcolor
= NULL
, **strings
,
577 int pseudoargc
, displayhelp
= 0, *booleans
;
578 const opt_t extra_opts
[] = {
579 {"osdcolor", OPT_ARG_MSTRZ
, &osdcolor
, NULL
, 0},
580 {"subcolor", OPT_ARG_MSTRZ
, &subcolor
, NULL
, 0},
581 {"help", OPT_ARG_BOOL
, &displayhelp
, NULL
, 0} };
582 opt_t
*subopts
= NULL
, *p
;
583 char * const strings_list
[] = {"-driver", "-kbddriver", "-mousedriver", "-font",
584 "-width", "-height", "-minwidth", "-minheight", "-maxwidth",
585 "-maxheight", "-recwidth", "-recheight", "-bright", "-contrast",
586 "-gamma", "-dimmul", "-boldmul", "-random" };
587 char * const booleans_list
[] = {"-dim", "-bold", "-reverse", "-normal",
588 "-boldfont", "-inverse", "-extended", "-eight", "-dither",
589 "-floyd_steinberg", "-error_distribution"};
590 char * const nobooleans_list
[] = {"-nodim", "-nobold", "-noreverse", "-nonormal",
591 "-noboldfont", "-noinverse", "-noextended", "-noeight", "-nodither",
592 "-nofloyd_steinberg", "-noerror_distribution"};
593 const int nstrings
= sizeof(strings_list
) / sizeof(char*);
594 const int nbooleans
= sizeof(booleans_list
) / sizeof(char*);
595 const int nextra_opts
= sizeof(extra_opts
) / sizeof(opt_t
);
596 const int nsubopts
= nstrings
+ nbooleans
+ nextra_opts
;
599 subopts
= calloc(nsubopts
+ 1, sizeof(opt_t
));
600 strings
= calloc(nstrings
, sizeof(char*));
601 booleans
= calloc(nbooleans
, sizeof(int));
604 for (i
=0; i
<nstrings
; i
++, p
++) {
605 p
->name
= strings_list
[i
] + 1; // skip '-'
606 p
->type
= OPT_ARG_MSTRZ
;
607 p
->valp
= &strings
[i
];
609 for (i
=0; i
<nbooleans
; i
++, p
++) {
610 p
->name
= booleans_list
[i
] + 1;
611 p
->type
= OPT_ARG_BOOL
;
612 p
->valp
= &booleans
[i
];
614 memcpy(p
, extra_opts
, sizeof(extra_opts
));
616 retval
= subopt_parse(arg
, subopts
);
618 if (retval
== 0 && displayhelp
) {
619 helpmsg
= strdup(aa_help
);
620 for (i
=0; i
<(signed)strlen(helpmsg
); i
++)
621 if (helpmsg
[i
] == '-') helpmsg
[i
] = ' ';
622 mp_msg(MSGT_VO
, MSGL_INFO
, MSGTR_VO_AA_HelpHeader
);
623 mp_msg(MSGT_VO
, MSGL_INFO
, "%s\n\n", helpmsg
);
624 mp_msg(MSGT_VO
, MSGL_INFO
, MSGTR_VO_AA_AdditionalOptions
);
628 pseudoargv
[3] = NULL
;
629 for (i
=0; i
<nstrings
; i
++) {
630 pseudoargc
= 3; // inside loop because aalib changes it
631 if (strings
[i
] != NULL
) {
632 pseudoargv
[1] = strings_list
[i
];
633 pseudoargv
[2] = strings
[i
];
634 aa_parseoptions(&aa_defparams
, &aa_defrenderparams
,
635 &pseudoargc
, pseudoargv
);
638 pseudoargv
[2] = NULL
;
639 for (i
=0; i
<nbooleans
; i
++) {
641 if (booleans
[i
]) pseudoargv
[1] = booleans_list
[i
];
642 else pseudoargv
[1] = nobooleans_list
[i
];
643 aa_parseoptions(&aa_defparams
, &aa_defrenderparams
,
644 &pseudoargc
, pseudoargv
);
646 if (osdcolor
) aaopt_osdcolor
= getcolor(osdcolor
);
647 if (subcolor
) aaopt_subcolor
= getcolor(subcolor
);
650 if (subopts
) free(subopts
);
651 if (booleans
) free(booleans
);
653 for (i
=0; i
<nstrings
; i
++)
658 if (osdcolor
) free(osdcolor
);
659 if (subcolor
) free(subcolor
);
660 if (helpmsg
) free(helpmsg
);
664 static int preinit(const char *arg
)
668 int fd
, vt
, major
, minor
;
674 if (parse_suboptions(arg
) != 0)
678 /* initializing of aalib */
680 hidis
=aa_getfirst(&aa_displayrecommended
);
682 /* check /dev/vcsa<vt> */
683 /* check only, if no driver is explicit set */
684 fd
= dup (fileno (stderr
));
686 major
= sbuf
.st_rdev
>> 8;
687 vt
= minor
= sbuf
.st_rdev
& 0xff;
689 sprintf (fname
, "/dev/vcsa%2.2i", vt
);
690 fp
= fopen (fname
, "w+");
692 fprintf(stderr
,"VO: [aa] cannot open %s for writing,"
693 "so we'll not use linux driver\n", fname
);
694 aa_recommendlowdisplay("linux");
695 aa_recommendhidisplay("curses");
696 aa_recommendhidisplay("X11");
698 } else aa_recommendhidisplay(hidis
);
699 c
= aa_autoinit(&aa_defparams
);
702 mp_msg(MSGT_VO
,MSGL_ERR
,"Cannot initialize aalib\n");
705 if (!aa_autoinitkbd(c
,0)) {
706 mp_msg(MSGT_VO
,MSGL_ERR
,"Cannot initialize keyboard\n");
711 aa_resizehandler(c
, (void *)resize
);
713 p
= aa_getrenderparams();
715 if ((strstr(c
->driver
->name
,"Curses")) || (strstr(c
->driver
->name
,"Linux"))){
716 freopen("/dev/null", "w", stderr
);
717 /* disable console blanking */
721 memset(image
,0,3*sizeof(uint8_t));
722 osdmessagetext
[0] = '\0';
728 static int control(uint32_t request
, void *data
)
731 case VOCTRL_QUERY_FORMAT
:
732 return query_format(*((uint32_t*)data
));
733 case VOCTRL_SET_EQUALIZER
: {
734 struct voctrl_set_equalizer_args
*args
= data
;
735 if (strcmp(args
->name
, "contrast") == 0)
736 p
->contrast
= (args
->value
+ 100) * 64 / 100;
737 else if (strcmp(args
->name
, "brightness") == 0)
738 p
->bright
= (args
->value
+ 100) * 128 / 100;
741 case VOCTRL_GET_EQUALIZER
: {
742 struct voctrl_get_equalizer_args
*args
= data
;
744 if (strcmp(args
->name
, "contrast") == 0)
745 *args
->valueptr
= (p
->contrast
- 64) * 100 / 64;
746 else if (strcmp(args
->name
, "brightness") == 0)
747 *args
->valueptr
= (p
->bright
- 128) * 100 / 128;