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
[MP_MAX_PLANES
];
72 static int image_stride
[MP_MAX_PLANES
];
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
;
102 * this function is called by aa lib if windows resizes
103 * further during init, because here we have to calculate
109 aspect_save_screenres(aa_imgwidth(c
),aa_imgheight(c
));
110 image_height
= aa_imgheight(c
); //src_height;
111 image_width
= aa_imgwidth(c
); //src_width;
113 aspect(&image_width
,&image_height
,A_ZOOM
);
115 image_x
= (aa_imgwidth(c
) - image_width
) / 2;
116 image_y
= (aa_imgheight(c
) - image_height
) / 2;
117 screen_w
= image_width
* aa_scrwidth(c
) / aa_imgwidth(c
);
118 screen_h
= image_height
* aa_scrheight(c
) / aa_imgheight(c
);
119 screen_x
= (aa_scrwidth(c
) - screen_w
) / 2;
120 screen_y
= (aa_scrheight(c
) - screen_h
) / 2;
122 if(sws
) sws_freeContext(sws
);
123 sws
= sws_getContextFromCmdLine(src_width
,src_height
,image_format
,
124 image_width
,image_height
,IMGFMT_Y8
);
126 memset(image
, 0, sizeof(image
));
127 image
[0] = aa_image(c
) + image_y
* aa_imgwidth(c
) + image_x
;
129 memset(image_stride
, 0, sizeof(image_stride
));
130 image_stride
[0] = aa_imgwidth(c
);
137 osdmessage(int duration
, int deko
, const char *fmt
, ...)
140 * for outputting a centered string at the bottom
141 * of our window for a while
144 char m
[MESSAGE_SIZE
];
145 unsigned int old_len
= strlen(osdmessagetext
);
148 vsprintf(m
, fmt
, ar
);
150 if (deko
==1) sprintf(osdmessagetext
, MESSAGE_DEKO
, m
);
151 else strcpy(osdmessagetext
, m
);
153 if(old_len
> strlen(osdmessagetext
)) {
154 memset(c
->textbuffer
+ osdy
* aa_scrwidth(c
) + osdx
,' ',old_len
);
155 memset(c
->attrbuffer
+ osdy
* aa_scrwidth(c
) + osdx
,0,old_len
);
158 stoposd
= time(NULL
) + duration
;
159 osdx
=(aa_scrwidth(c
) / 2) - (strlen(osdmessagetext
) / 2 ) ;
164 osdpercent(int duration
, int deko
, int min
, int max
, int val
, const char * desc
, const char * unit
)
167 * prints a bar for setting values
174 step
=(float)aa_scrwidth(c
) /(float)(max
-min
);
175 where
=(val
-min
)*step
;
176 osdmessage(duration
,deko
,"%s: %i%s",desc
, val
, unit
);
178 posbar
[aa_scrwidth(c
)-1]='|';
179 for (i
=0;i
<aa_scrwidth(c
);i
++){
180 if (i
==where
) posbar
[i
]='#';
183 if (where
!=0) posbar
[0]='|';
184 if (where
!=(aa_scrwidth(c
)-1) ) posbar
[aa_scrwidth(c
)-1]='|';
186 posbar
[aa_scrwidth(c
)]='\0';
193 if(osd_text_length
> 0 && !vo_osd_text
) {
194 memset(c
->textbuffer
,' ',osd_text_length
);
195 memset(c
->attrbuffer
,0,osd_text_length
);
199 * places the mplayer status osd
201 if (vo_osd_text
&& vo_osd_text
[0] != 0) {
203 if(vo_osd_text
[0] < 32) {
204 len
= strlen(sub_osd_names_short
[vo_osd_text
[0]]) + strlen(vo_osd_text
+1) + 2;
205 aa_printf(c
, 0, 0 , aaopt_osdcolor
, "%s %s ", sub_osd_names_short
[vo_osd_text
[0]], vo_osd_text
+1);
207 len
= strlen(vo_osd_text
) + 1;
208 aa_printf(c
, 0, 0 , aaopt_osdcolor
, "%s ",vo_osd_text
);
211 if(len
< osd_text_length
) {
212 memset(c
->textbuffer
+ len
,' ',osd_text_length
- len
);
213 memset(c
->attrbuffer
+ len
,0,osd_text_length
- len
);
215 osd_text_length
= len
;
221 printosdprogbar(void){
222 /* print mplayer osd-progbar */
223 if (vo_osd_progbar_type
!=-1){
224 osdpercent(1,1,0,255,vo_osd_progbar_value
, sub_osd_names
[vo_osd_progbar_type
], "");
228 config(uint32_t width
, uint32_t height
, uint32_t d_width
,
229 uint32_t d_height
, uint32_t flags
, char *title
,
238 aspect_save_orig(width
,height
);
239 aspect_save_prescale(d_width
,d_height
);
243 image_format
= format
;
245 /* nothing will change its size, be we need some values initialized */
248 /* now init our own 'font' */
249 if(!vo_font_save
) vo_font_save
= vo_font
;
250 if(vo_font
== vo_font_save
) {
251 vo_font
=malloc(sizeof(font_desc_t
));//if(!desc) return NULL;
252 memset(vo_font
,0,sizeof(font_desc_t
));
253 vo_font
->pic_a
[0]=malloc(sizeof(raw_file
));
254 memset(vo_font
->pic_a
[0],0,sizeof(raw_file
));
255 vo_font
->pic_b
[0]=malloc(sizeof(raw_file
));
256 memset(vo_font
->pic_b
[0],0,sizeof(raw_file
));
258 #ifdef CONFIG_FREETYPE
259 vo_font
->dynamic
= 0;
262 vo_font
->spacewidth
=1;
263 vo_font
->charspace
=0;
265 vo_font
->pic_a
[0]->bmp
=malloc(255);
266 vo_font
->pic_a
[0]->pal
=NULL
;
267 vo_font
->pic_b
[0]->bmp
=malloc(255);
268 vo_font
->pic_b
[0]->pal
=NULL
;
269 vo_font
->pic_a
[0]->w
=1;
270 vo_font
->pic_a
[0]->h
=1;
271 for (i
=0; i
<255; i
++){
275 vo_font
->pic_a
[0]->bmp
[i
]=i
;
276 vo_font
->pic_b
[0]->bmp
[i
]=i
;
281 osdmessage(5, 1, "Welcome to ASCII ART MPlayer");
283 mp_msg(MSGT_VO
,MSGL_V
,"VO: [aa] screendriver: %s\n", c
->driver
->name
);
284 mp_msg(MSGT_VO
,MSGL_V
,"VO: [aa] keyboarddriver: %s\n", c
->kbddriver
->name
);
286 mp_msg(MSGT_VO
,MSGL_INFO
,
288 "Important suboptions\n"
289 "\textended use use all 256 characters\n"
290 "\teight use eight bit ascii\n"
291 "\tdriver set recommended aalib driver (X11,curses,linux)\n"
292 "\thelp to see all options provided by aalib\n"
297 "\t3 : brightness -\n"
298 "\t4 : brightness +\n"
299 "\t5 : fast rendering\n"
301 "\t7 : invert image\n"
302 "\ta : toggles between aa and mplayer control\n"
305 "All other keys are MPlayer defaults.\n"
314 query_format(uint32_t format
) {
316 * ...are we able to... ?
318 * All input format supported by the sws
333 return VFCAP_CSP_SUPPORTED
| VFCAP_SWSCALE
| VFCAP_OSD
;
339 draw_frame(uint8_t *src
[]) {
340 int stride
[MP_MAX_PLANES
] = {0};
342 switch(image_format
) {
345 stride
[0] = src_width
*2;
349 stride
[0] = src_width
*3;
352 stride
[0] = src_width
*4;
356 sws_scale(sws
,src
,stride
,0,src_height
,image
,image_stride
);
358 /* Now 'ASCIInate' the image */
360 aa_fastrender(c
, screen_x
, screen_y
, screen_w
+ screen_x
, screen_h
+ screen_y
);
362 aa_render(c
, p
,screen_x
, screen_y
, screen_w
+ screen_x
, screen_h
+ screen_y
);
368 draw_slice(uint8_t *src
[], int stride
[],
369 int w
, int h
, int x
, int y
) {
371 int dx1
= screen_x
+ (x
* screen_w
/ src_width
);
372 int dy1
= screen_y
+ (y
* screen_h
/ src_height
);
373 int dx2
= screen_x
+ ((x
+w
) * screen_w
/ src_width
);
374 int dy2
= screen_y
+ ((y
+h
) * screen_h
/ src_height
);
376 sws_scale(sws
,src
,stride
,y
,h
,image
,image_stride
);
378 /* Now 'ASCIInate' the image */
380 aa_fastrender(c
, dx1
, dy1
, dx2
, dy2
);
382 aa_render(c
, p
,dx1
, dy1
, dx2
, dy2
);
391 /* do we have to put *our* (messages, progbar) osd to aa's txtbuf ? */
394 if (time(NULL
)>=stoposd
) {
396 if(*osdmessagetext
) {
397 memset(c
->textbuffer
+ osdy
* aa_scrwidth(c
) + osdx
,' ',strlen(osdmessagetext
));
398 memset(c
->attrbuffer
+ osdy
* aa_scrwidth(c
) + osdx
,0,strlen(osdmessagetext
));
399 osdmessagetext
[0] = '\0';
402 memset(c
->textbuffer
+ (osdy
+1) * aa_scrwidth(c
),' ',strlen(posbar
));
403 memset(c
->attrbuffer
+ (osdy
+1) * aa_scrwidth(c
),0,strlen(posbar
));
407 aa_puts(c
, osdx
, osdy
, AA_SPECIAL
, osdmessagetext
);
410 aa_puts(c
, 0, osdy
+ 1, AA_SPECIAL
, posbar
);
413 /* OSD time & playmode , subtitles */
425 * called by show_image and mplayer
428 while ((key
=aa_getevent(c
,0))!=AA_NONE
){
430 /* some conversations */
433 mplayer_put_key(KEY_UP
);
436 mplayer_put_key(KEY_DOWN
);
439 mplayer_put_key(KEY_LEFT
);
442 mplayer_put_key(KEY_RIGHT
);
445 mplayer_put_key(KEY_ESC
);
448 mplayer_put_key(KEY_PAGE_UP
);
451 mplayer_put_key(KEY_PAGE_DOWN
);
454 continue; /* aa lib special key */
458 if (key
=='a' || key
=='A'){
459 aaconfigmode
=!aaconfigmode
;
460 osdmessage(MESSAGE_DURATION
, 1, "aa config mode is now %s",
461 aaconfigmode
==1 ? "on. use keys 5-7" : "off");
463 if (aaconfigmode
==1) {
465 /* AA image controls */
468 osdmessage(MESSAGE_DURATION
, 1, "Fast mode is now %s", fast
==1 ? "on" : "off");
471 if (p
->dither
==AA_FLOYD_S
){
473 osdmessage(MESSAGE_DURATION
, 1, "Dithering: Off");
474 }else if (p
->dither
==AA_NONE
){
475 p
->dither
=AA_ERRORDISTRIB
;
476 osdmessage(MESSAGE_DURATION
, 1, "Dithering: Error Distribution");
477 }else if (p
->dither
==AA_ERRORDISTRIB
){
478 p
->dither
=AA_FLOYD_S
;
479 osdmessage(MESSAGE_DURATION
, 1, "Dithering: Floyd Steinberg");
483 p
->inversion
=!p
->inversion
;
484 osdmessage(MESSAGE_DURATION
, 1, "Invert mode is now %s",
485 p
->inversion
==1 ? "on" : "off");
489 /* nothing if we're interested in?
490 * the mplayer should handle it!
492 mplayer_put_key(key
);
496 else mplayer_put_key(key
);
506 if (strstr(c
->driver
->name
,"Curses") || strstr(c
->driver
->name
,"Linux")){
507 freopen("/dev/tty", "w", stderr
);
510 free(vo_font
->pic_a
[0]->bmp
);
511 free(vo_font
->pic_a
[0]);
512 free(vo_font
->pic_b
[0]->bmp
);
513 free(vo_font
->pic_b
[0]);
515 vo_font
= vo_font_save
;
521 static void draw_alpha(int x
,int y
, int w
,int h
, unsigned char* src
, unsigned char *srca
, int stride
){
523 for (i
= 0; i
< h
; i
++) {
524 for (j
= 0; j
< w
; j
++) {
525 if (src
[i
*stride
+j
] > 0) {
526 c
->textbuffer
[x
+ j
+ (y
+i
)*aa_scrwidth(c
)] = src
[i
*stride
+j
];
527 c
->attrbuffer
[x
+ j
+ (y
+i
)*aa_scrwidth(c
)] = aaopt_subcolor
;
533 static void clear_alpha(int x0
,int y0
, int w
,int h
) {
536 for(l
= 0 ; l
< h
; l
++) {
537 memset(c
->textbuffer
+ (y0
+ l
) * aa_scrwidth(c
) + x0
,' ',w
);
538 memset(c
->attrbuffer
+ (y0
+ l
) * aa_scrwidth(c
) + x0
,0,w
);
545 char * vo_osd_text_save
;
546 int vo_osd_progbar_type_save
;
549 /* let vo_draw_text only write subtitle */
550 vo_osd_text_save
=vo_osd_text
; /* we have to save the osd_text */
552 vo_osd_progbar_type_save
=vo_osd_progbar_type
;
553 vo_osd_progbar_type
=-1;
554 vo_remove_text(aa_scrwidth(c
), aa_scrheight(c
),clear_alpha
);
555 vo_draw_text(aa_scrwidth(c
), aa_scrheight(c
), draw_alpha
);
556 vo_osd_text
=vo_osd_text_save
;
557 vo_osd_progbar_type
=vo_osd_progbar_type_save
;
564 if (s
==NULL
) return -1;
565 i
=strtol(s
, &rest
, 10);
566 if ((rest
==NULL
|| strlen(rest
)==0) && i
>=0 && i
<=5) return i
;
567 if (!strcasecmp(s
, "normal")) return AA_NORMAL
;
568 else if (!strcasecmp(s
, "dim")) return AA_DIM
;
569 else if (!strcasecmp(s
, "bold")) return AA_BOLD
;
570 else if (!strcasecmp(s
, "boldfont")) return AA_BOLDFONT
;
571 else if (!strcasecmp(s
, "special")) return AA_SPECIAL
;
575 static int parse_suboptions(const char *arg
) {
576 char *pseudoargv
[4], *osdcolor
= NULL
, *subcolor
= NULL
, **strings
,
578 int pseudoargc
, displayhelp
= 0, *booleans
;
579 const opt_t extra_opts
[] = {
580 {"osdcolor", OPT_ARG_MSTRZ
, &osdcolor
, NULL
},
581 {"subcolor", OPT_ARG_MSTRZ
, &subcolor
, NULL
},
582 {"help", OPT_ARG_BOOL
, &displayhelp
, NULL
} };
583 opt_t
*subopts
= NULL
, *p
;
584 char * const strings_list
[] = {"-driver", "-kbddriver", "-mousedriver", "-font",
585 "-width", "-height", "-minwidth", "-minheight", "-maxwidth",
586 "-maxheight", "-recwidth", "-recheight", "-bright", "-contrast",
587 "-gamma", "-dimmul", "-boldmul", "-random" };
588 char * const booleans_list
[] = {"-dim", "-bold", "-reverse", "-normal",
589 "-boldfont", "-inverse", "-extended", "-eight", "-dither",
590 "-floyd_steinberg", "-error_distribution"};
591 char * const nobooleans_list
[] = {"-nodim", "-nobold", "-noreverse", "-nonormal",
592 "-noboldfont", "-noinverse", "-noextended", "-noeight", "-nodither",
593 "-nofloyd_steinberg", "-noerror_distribution"};
594 const int nstrings
= sizeof(strings_list
) / sizeof(char*);
595 const int nbooleans
= sizeof(booleans_list
) / sizeof(char*);
596 const int nextra_opts
= sizeof(extra_opts
) / sizeof(opt_t
);
597 const int nsubopts
= nstrings
+ nbooleans
+ nextra_opts
;
600 subopts
= calloc(nsubopts
+ 1, sizeof(opt_t
));
601 strings
= calloc(nstrings
, sizeof(char*));
602 booleans
= calloc(nbooleans
, sizeof(int));
605 for (i
=0; i
<nstrings
; i
++, p
++) {
606 p
->name
= strings_list
[i
] + 1; // skip '-'
607 p
->type
= OPT_ARG_MSTRZ
;
608 p
->valp
= &strings
[i
];
610 for (i
=0; i
<nbooleans
; i
++, p
++) {
611 p
->name
= booleans_list
[i
] + 1;
612 p
->type
= OPT_ARG_BOOL
;
613 p
->valp
= &booleans
[i
];
615 memcpy(p
, extra_opts
, sizeof(extra_opts
));
617 retval
= subopt_parse(arg
, subopts
);
619 if (retval
== 0 && displayhelp
) {
620 helpmsg
= strdup(aa_help
);
621 for (i
=0; i
<(signed)strlen(helpmsg
); i
++)
622 if (helpmsg
[i
] == '-') helpmsg
[i
] = ' ';
623 mp_msg(MSGT_VO
, MSGL_INFO
, MSGTR_VO_AA_HelpHeader
);
624 mp_msg(MSGT_VO
, MSGL_INFO
, "%s\n\n", helpmsg
);
625 mp_msg(MSGT_VO
, MSGL_INFO
, MSGTR_VO_AA_AdditionalOptions
);
629 pseudoargv
[3] = NULL
;
630 for (i
=0; i
<nstrings
; i
++) {
631 pseudoargc
= 3; // inside loop because aalib changes it
632 if (strings
[i
] != NULL
) {
633 pseudoargv
[1] = strings_list
[i
];
634 pseudoargv
[2] = strings
[i
];
635 aa_parseoptions(&aa_defparams
, &aa_defrenderparams
,
636 &pseudoargc
, pseudoargv
);
639 pseudoargv
[2] = NULL
;
640 for (i
=0; i
<nbooleans
; i
++) {
642 if (booleans
[i
]) pseudoargv
[1] = booleans_list
[i
];
643 else pseudoargv
[1] = nobooleans_list
[i
];
644 aa_parseoptions(&aa_defparams
, &aa_defrenderparams
,
645 &pseudoargc
, pseudoargv
);
647 if (osdcolor
) aaopt_osdcolor
= getcolor(osdcolor
);
648 if (subcolor
) aaopt_subcolor
= getcolor(subcolor
);
651 if (subopts
) free(subopts
);
652 if (booleans
) free(booleans
);
654 for (i
=0; i
<nstrings
; i
++)
659 if (osdcolor
) free(osdcolor
);
660 if (subcolor
) free(subcolor
);
661 if (helpmsg
) free(helpmsg
);
665 static int preinit(const char *arg
)
669 int fd
, vt
, major
, minor
;
675 if (parse_suboptions(arg
) != 0)
679 /* initializing of aalib */
681 hidis
=aa_getfirst(&aa_displayrecommended
);
683 /* check /dev/vcsa<vt> */
684 /* check only, if no driver is explicit set */
685 fd
= dup (fileno (stderr
));
687 major
= sbuf
.st_rdev
>> 8;
688 vt
= minor
= sbuf
.st_rdev
& 0xff;
690 sprintf (fname
, "/dev/vcsa%2.2i", vt
);
691 fp
= fopen (fname
, "w+");
693 fprintf(stderr
,"VO: [aa] cannot open %s for writing,"
694 "so we'll not use linux driver\n", fname
);
695 aa_recommendlowdisplay("linux");
696 aa_recommendhidisplay("curses");
697 aa_recommendhidisplay("X11");
699 } else aa_recommendhidisplay(hidis
);
700 c
= aa_autoinit(&aa_defparams
);
703 mp_msg(MSGT_VO
,MSGL_ERR
,"Cannot initialize aalib\n");
706 if (!aa_autoinitkbd(c
,0)) {
707 mp_msg(MSGT_VO
,MSGL_ERR
,"Cannot initialize keyboard\n");
712 aa_resizehandler(c
, (void *)resize
);
714 p
= aa_getrenderparams();
716 if ((strstr(c
->driver
->name
,"Curses")) || (strstr(c
->driver
->name
,"Linux"))){
717 freopen("/dev/null", "w", stderr
);
718 /* disable console blanking */
722 memset(image
,0,3*sizeof(uint8_t));
723 osdmessagetext
[0] = '\0';
729 static int control(uint32_t request
, void *data
, ...)
732 case VOCTRL_QUERY_FORMAT
:
733 return query_format(*((uint32_t*)data
));
734 case VOCTRL_SET_EQUALIZER
: {
739 val
= va_arg(ap
, int);
742 if(strcmp((char*)data
,"contrast") == 0)
743 p
->contrast
= ( val
+ 100 ) * 64 / 100;
744 else if(strcmp((char*)data
,"brightness") == 0)
745 p
->bright
= ( val
+ 100) * 128 / 100;
748 case VOCTRL_GET_EQUALIZER
: {
753 val
= va_arg(ap
, int*);
756 if(strcmp((char*)data
,"contrast") == 0)
757 *val
= (p
->contrast
- 64) * 100 / 64;
758 else if(strcmp((char*)data
,"brightness") == 0)
759 *val
= (p
->bright
- 128) * 100 / 128;