audio: improve decoder open failure handling
[mplayer.git] / sub / osd_libass.c
blobdae1ed00bbb2a65eb5c2648a29ce6d9ed49bf2bf
1 /*
2 * This file is part of mplayer2.
4 * mplayer2 is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * mplayer2 is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with mplayer2. If not, see <http://www.gnu.org/licenses/>.
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <assert.h>
23 #include "config.h"
25 #include "talloc.h"
26 #include "mp_msg.h"
27 #include "sub.h"
28 #include "libavutil/common.h"
30 static const char osd_font_pfb[] =
31 #include "sub/osd_font.h"
34 #include "sub/ass_mp.h"
35 #include "mp_core.h"
38 // Map OSD symbols (e.g. OSD_PLAY) to the glyphs in osd_font_pfb[].
39 #define OSD_CODEPOINTS 0xE000
41 void osd_init_backend(struct osd_state *osd)
43 /* The player adds file-specific embedded fonts with ass_add_font()
44 * and clears them later with ass_clear_fonts(). Since these
45 * operations affect the whole library, this would lose the OSD font.
46 * So we use a separate ass_library instance for OSD rendering.
48 osd->osd_ass_library = mp_ass_init(osd->opts);
49 ass_add_font(osd->osd_ass_library, "OSD", (void *)osd_font_pfb,
50 sizeof(osd_font_pfb) - 1);
52 osd->osd_render = ass_renderer_init(osd->osd_ass_library);
53 mp_ass_configure_fonts(osd->osd_render);
54 ass_set_aspect_ratio(osd->osd_render, 1.0, 1.0);
57 void osd_destroy_backend(struct osd_state *osd)
59 if (osd) {
60 if (osd->osd_render)
61 ass_renderer_done(osd->osd_render);
62 osd->osd_render = NULL;
63 ass_library_done(osd->osd_ass_library);
64 osd->osd_ass_library = NULL;
68 static void eosd_draw_alpha_a8i8(unsigned char *src,
69 int src_w, int src_h,
70 int src_stride,
71 unsigned char *dst_a,
72 unsigned char *dst_i,
73 size_t dst_stride,
74 int dst_x, int dst_y,
75 uint32_t color)
77 const unsigned int r = (color >> 24) & 0xff;
78 const unsigned int g = (color >> 16) & 0xff;
79 const unsigned int b = (color >> 8) & 0xff;
80 const unsigned int a = 0xff - (color & 0xff);
82 int gray = (r + g + b) / 3; // not correct
84 dst_a += dst_y * dst_stride + dst_x;
85 dst_i += dst_y * dst_stride + dst_x;
87 for (int y = 0; y < src_h; y++) {
88 for (int x = 0; x < src_w; x++) {
89 unsigned char as = ((*src) * a + 255) >> 8;
90 unsigned char bs = (gray * as + 255) >> 8;
91 /* In the old mplayer OSD format, alpha=0 means transparency,
92 * but alpha=1..255 is opaque..transparent. */
93 *dst_i = (*dst_i * (255-as) >> 8) + bs;
94 unsigned char old_transparency = *dst_a - 1;
95 *dst_a = (old_transparency * (255-as) + 255 >> 8) + 1;
97 dst_a++;
98 dst_i++;
99 src++;
101 dst_a += dst_stride - src_w;
102 dst_i += dst_stride - src_w;
103 src += src_stride - src_w;
107 static void eosd_render_a8i8(unsigned char *a, unsigned char *i, size_t stride,
108 int x, int y, ASS_Image *imgs)
110 for (ASS_Image *p = imgs; p; p = p->next) {
111 eosd_draw_alpha_a8i8(p->bitmap, p->w, p->h, p->stride, a, i, stride,
112 x + p->dst_x, y + p->dst_y, p->color);
116 static bool ass_bb(ASS_Image *imgs, int *x1, int *y1, int *x2, int *y2)
118 *x1 = *y1 = INT_MAX;
119 *x2 = *y2 = INT_MIN;
120 for (ASS_Image *p = imgs; p; p = p->next) {
121 *x1 = FFMIN(*x1, p->dst_x);
122 *y1 = FFMIN(*y1, p->dst_y);
123 *x2 = FFMAX(*x2, p->dst_x + p->w);
124 *y2 = FFMAX(*y2, p->dst_y + p->h);
126 return *x1 < *x2 && *y1 < *y2;
129 static void draw_ass_osd(struct osd_state *osd, mp_osd_obj_t *obj)
131 ass_set_frame_size(osd->osd_render, osd->w, osd->h);
133 ASS_Image *imgs = ass_render_frame(osd->osd_render, obj->osd_track, 0,
134 NULL);
136 int x1, y1, x2, y2;
137 if (!ass_bb(imgs, &x1, &y1, &x2, &y2)) {
138 obj->flags &= ~OSDFLAG_VISIBLE;
139 return;
142 obj->bbox.x1 = x1;
143 obj->bbox.y1 = y1;
144 obj->bbox.x2 = x2;
145 obj->bbox.y2 = y2;
146 obj->flags |= OSDFLAG_BBOX;
147 osd_alloc_buf(obj);
149 eosd_render_a8i8(obj->alpha_buffer, obj->bitmap_buffer, obj->stride,
150 -x1, -y1, imgs);
154 static void update_font_scale(ASS_Track *track, ASS_Style *style, double factor)
156 // duplicated from ass_mp.c
157 double fs = track->PlayResY * factor / 100.;
158 /* The font size is always proportional to video height only;
159 * real -subfont-autoscale behavior is not implemented.
160 * Apply a correction that corresponds to about 4:3 aspect ratio
161 * video to get a size somewhat closer to what non-libass rendering
162 * would produce with the same text_font_scale_factor
163 * and subtitle_autoscale.
165 if (subtitle_autoscale == 2)
166 fs *= 1.3;
167 else if (subtitle_autoscale == 3)
168 fs *= 1.7;
169 style->FontSize = fs;
170 style->Outline = style->FontSize / 16;
174 static ASS_Track *create_osd_ass_track(struct osd_state *osd)
176 ASS_Track *track = mp_ass_default_track(osd->osd_ass_library, osd->opts);
177 ASS_Style *style = track->styles + track->default_style;
179 track->PlayResX = track->PlayResY * 1.33333;
181 update_font_scale(track, style, osd_font_scale_factor);
183 style->Alignment = 5;
185 free(style->FontName);
186 style->FontName = strdup(font_name ? font_name : "Sans");
188 return track;
191 static ASS_Event *get_osd_ass_event(ASS_Track *track)
193 ass_flush_events(track);
194 ass_alloc_event(track);
195 ASS_Event *event = track->events + 0;
196 event->Start = 0;
197 event->Duration = 100;
198 event->Style = track->default_style;
199 return event;
202 static char *append_utf8_buffer(char *buffer, uint32_t codepoint)
204 char data[8];
205 uint8_t tmp;
206 char *output = data;
207 PUT_UTF8(codepoint, tmp, *output++ = tmp;);
208 return talloc_strndup_append_buffer(buffer, data, output - data);
211 void osd_get_function_sym(char *buffer, size_t buffer_size, int osd_function)
213 // 0xFF is never valid UTF-8, so we can use it to escape OSD symbols.
214 snprintf(buffer, buffer_size, "\xFF%c", osd_function);
217 static char *mangle_ass(const char *in)
219 char *res = talloc_strdup(NULL, "");
220 while (*in) {
221 // As used by osd_get_function_sym().
222 if (in[0] == '\xFF' && in[1]) {
223 /* The special play/pause etc symbols are taller than normal
224 * letters, and do not look very good when libass renders them
225 * with the base of the letters at the same level. Scale
226 * the symbols down to 70% size so they're closer to the
227 * letter sizes in usual fonts. */
228 res = talloc_strdup_append_buffer(res, "{\\fnOSD\\fscx70\\fscy70}");
229 res = append_utf8_buffer(res, OSD_CODEPOINTS + in[1]);
230 res = talloc_strdup_append_buffer(res, "{\\r}");
231 in += 2;
232 continue;
234 if (*in == '{')
235 res = talloc_strdup_append_buffer(res, "\\");
236 res = talloc_strndup_append_buffer(res, in, 1);
237 /* Libass has a couple of escapes like "\n" and "\{", but only
238 * those specific cases are recognized, and there is no escape
239 * for "\" itself. This makes it impossible to quote strings
240 * like "\n"; for example, "\\n" would be interpreted as a
241 * literal "\" followed by "\n" escape for linefeed. Work around
242 * this by adding the ZERO WIDTH NO-BREAK SPACE character after
243 * each "\". This ensures it will not be interpreted as an escape
244 * and should not change visible output.
246 if (*in == '\\')
247 res = append_utf8_buffer(res, 0xfeff);
248 in++;
250 return res;
253 void vo_update_text_osd(struct osd_state *osd, mp_osd_obj_t* obj)
255 if (!obj->osd_track)
256 obj->osd_track = create_osd_ass_track(osd);
257 ASS_Event *event = get_osd_ass_event(obj->osd_track);
258 event->Text = mangle_ass(osd->osd_text);
259 draw_ass_osd(osd, obj);
260 talloc_free(event->Text);
261 event->Text = NULL;
264 #define OSDBAR_ELEMS 46
266 void vo_update_text_progbar(struct osd_state *osd, mp_osd_obj_t* obj)
268 obj->flags |= OSDFLAG_CHANGED | OSDFLAG_VISIBLE;
270 if (vo_osd_progbar_type < 0) {
271 obj->flags &= ~OSDFLAG_VISIBLE;
272 return;
275 if (!obj->osd_track)
276 obj->osd_track = create_osd_ass_track(osd);
278 ASS_Style *style = obj->osd_track->styles + obj->osd_track->default_style;
280 style->Alignment = 10;
281 style->MarginL = style->MarginR = style->MarginV = 0;
283 // We need a fixed font size with respect to the OSD width.
284 // Assume the OSD bar takes 2/3 of the OSD width at PlayResY=288 and
285 // FontSize=17 with an OSD aspect ratio of 16:9. Rescale as needed.
286 // xxx can fail when unknown fonts are involved
287 double asp = (double)osd->w / osd->h;
288 double scale = (asp / 1.77777) * (obj->osd_track->PlayResY / 288.0);
289 style->ScaleX = style->ScaleY = scale;
290 style->FontSize = 17.0;
291 style->Outline = style->FontSize / 16 * scale;
293 int active = (vo_osd_progbar_value * OSDBAR_ELEMS + 255) / 256;
294 active = FFMIN(OSDBAR_ELEMS, FFMAX(active, 0));
296 char *text = talloc_strdup(NULL, "{\\q2}");
298 if (vo_osd_progbar_type >= 32) {
299 text = append_utf8_buffer(text, vo_osd_progbar_type);
300 } else if (vo_osd_progbar_type > 0) {
301 text = talloc_strdup_append_buffer(text, "{\\fnOSD}");
302 text = append_utf8_buffer(text, OSD_CODEPOINTS + vo_osd_progbar_type);
303 text = talloc_strdup_append_buffer(text, "{\\r}");
306 //xxx space in normal font, because OSD font doesn't have a space
307 text = talloc_strdup_append_buffer(text, "\\h");
308 text = talloc_strdup_append_buffer(text, "{\\fnOSD}");
310 text = append_utf8_buffer(text, OSD_CODEPOINTS + OSD_PB_START);
311 for (int n = 0; n < active; n++)
312 text = append_utf8_buffer(text, OSD_CODEPOINTS + OSD_PB_0);
313 for (int n = 0; n < OSDBAR_ELEMS - active; n++)
314 text = append_utf8_buffer(text, OSD_CODEPOINTS + OSD_PB_1);
315 text = append_utf8_buffer(text, OSD_CODEPOINTS + OSD_PB_END);
317 ASS_Event *event = get_osd_ass_event(obj->osd_track);
318 event->Text = text;
319 draw_ass_osd(osd, obj);
320 event->Text = NULL;
322 talloc_free(text);
325 void vo_update_text_sub(struct osd_state *osd, mp_osd_obj_t* obj)
327 struct MPOpts *opts = osd->opts;
329 obj->flags |= OSDFLAG_CHANGED | OSDFLAG_VISIBLE;
331 if (!vo_sub || !opts->sub_visibility) {
332 obj->flags &= ~OSDFLAG_VISIBLE;
333 return;
336 if (!obj->osd_track)
337 obj->osd_track = mp_ass_default_track(osd->osd_ass_library, osd->opts);
339 ASS_Style *style = obj->osd_track->styles + obj->osd_track->default_style;
341 style->MarginV = obj->osd_track->PlayResY * ((100 - sub_pos)/110.0);
342 update_font_scale(obj->osd_track, style, text_font_scale_factor);
344 char *text = talloc_strdup(NULL, "");
346 for (int n = 0; n < vo_sub->lines; n++)
347 text = talloc_asprintf_append_buffer(text, "%s\n", vo_sub->text[n]);
349 ASS_Event *event = get_osd_ass_event(obj->osd_track);
350 event->Text = mangle_ass(text);
351 draw_ass_osd(osd, obj);
352 talloc_free(event->Text);
353 event->Text = NULL;
355 talloc_free(text);
358 // Unimplemented.
359 void vo_update_text_teletext(struct osd_state *osd, mp_osd_obj_t *obj)
361 obj->flags |= OSDFLAG_CHANGED;
362 obj->flags &= ~OSDFLAG_VISIBLE;
363 if (!vo_osd_teletext_page || !vo_osd_teletext_mode)
364 return;
365 mp_msg(MSGT_OSD, MSGL_ERR, "OSD: teletext rendering not implemented\n");