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 vo_info_t info
= {
50 "Alban Bedel <albeu@free.fr> and Folke Ashberg <folke@ashberg.de>",
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
;
85 /* our version of the playmodes :) */
87 /* to disable stdout outputs when curses/linux mode */
91 int aaopt_osdcolor
= AA_SPECIAL
;
92 int aaopt_subcolor
= AA_SPECIAL
;
94 extern struct aa_hardware_params aa_defparams
;
95 extern struct aa_renderparams aa_defrenderparams
;
100 * this function is called by aa lib if windows resizes
101 * further during init, because here we have to calculate
107 aspect_save_screenres(aa_imgwidth(c
),aa_imgheight(c
));
108 image_height
= aa_imgheight(c
); //src_height;
109 image_width
= aa_imgwidth(c
); //src_width;
111 aspect(&image_width
,&image_height
,A_ZOOM
);
113 image_x
= (aa_imgwidth(c
) - image_width
) / 2;
114 image_y
= (aa_imgheight(c
) - image_height
) / 2;
115 screen_w
= image_width
* aa_scrwidth(c
) / aa_imgwidth(c
);
116 screen_h
= image_height
* aa_scrheight(c
) / aa_imgheight(c
);
117 screen_x
= (aa_scrwidth(c
) - screen_w
) / 2;
118 screen_y
= (aa_scrheight(c
) - screen_h
) / 2;
120 if(sws
) sws_freeContext(sws
);
121 sws
= sws_getContextFromCmdLine(src_width
,src_height
,image_format
,
122 image_width
,image_height
,IMGFMT_Y8
);
124 image
[0] = aa_image(c
) + image_y
* aa_imgwidth(c
) + image_x
;
128 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 out own 'font' (to use vo_draw_text_sub without edit them) */
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
));
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
[3] = { 0 , 0 , 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_ordered(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_ordered(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
) {
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 opt_t extra_opts
[] = {
580 {"osdcolor", OPT_ARG_MSTRZ
, &osdcolor
, NULL
, 0},
581 {"subcolor", OPT_ARG_MSTRZ
, &subcolor
, NULL
, 0},
582 {"help", OPT_ARG_BOOL
, &displayhelp
, NULL
, 0} };
583 opt_t
*subopts
= NULL
, *p
;
584 char *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 *booleans_list
[] = {"-dim", "-bold", "-reverse", "-normal",
589 "-boldfont", "-inverse", "-extended", "-eight", "-dither",
590 "-floyd_steinberg", "-error_distribution"};
591 char *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(int);
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
;
672 extern aa_linkedlist
*aa_displayrecommended
;
676 if (parse_suboptions(arg
) != 0)
680 /* initializing of aalib */
682 hidis
=aa_getfirst(&aa_displayrecommended
);
684 /* check /dev/vcsa<vt> */
685 /* check only, if no driver is explicit set */
686 fd
= dup (fileno (stderr
));
688 major
= sbuf
.st_rdev
>> 8;
689 vt
= minor
= sbuf
.st_rdev
& 0xff;
691 sprintf (fname
, "/dev/vcsa%2.2i", vt
);
692 fp
= fopen (fname
, "w+");
694 fprintf(stderr
,"VO: [aa] cannot open %s for writing,"
695 "so we'll not use linux driver\n", fname
);
696 aa_recommendlowdisplay("linux");
697 aa_recommendhidisplay("curses");
698 aa_recommendhidisplay("X11");
700 } else aa_recommendhidisplay(hidis
);
701 c
= aa_autoinit(&aa_defparams
);
704 mp_msg(MSGT_VO
,MSGL_ERR
,"Cannot initialize aalib\n");
707 if (!aa_autoinitkbd(c
,0)) {
708 mp_msg(MSGT_VO
,MSGL_ERR
,"Cannot initialize keyboard\n");
713 aa_resizehandler(c
, (void *)resize
);
715 p
= aa_getrenderparams();
717 if ((strstr(c
->driver
->name
,"Curses")) || (strstr(c
->driver
->name
,"Linux"))){
718 freopen("/dev/null", "w", stderr
);
719 /* disable console blanking */
723 memset(image
,0,3*sizeof(uint8_t));
724 osdmessagetext
[0] = '\0';
730 static int control(uint32_t request
, void *data
, ...)
733 case VOCTRL_QUERY_FORMAT
:
734 return query_format(*((uint32_t*)data
));
735 case VOCTRL_SET_EQUALIZER
: {
740 val
= va_arg(ap
, int);
743 if(strcmp((char*)data
,"contrast") == 0)
744 p
->contrast
= ( val
+ 100 ) * 64 / 100;
745 else if(strcmp((char*)data
,"brightness") == 0)
746 p
->bright
= ( val
+ 100) * 128 / 100;
749 case VOCTRL_GET_EQUALIZER
: {
754 val
= va_arg(ap
, int*);
757 if(strcmp((char*)data
,"contrast") == 0)
758 *val
= (p
->contrast
- 64) * 100 / 64;
759 else if(strcmp((char*)data
,"brightness") == 0)
760 *val
= (p
->bright
- 128) * 100 / 128;