0
0
mirror of https://github.com/mpv-player/mpv.git synced 2024-09-20 03:52:22 +02:00

sub: add sd_spu.c to wrap spudec, cleanup mplayer.c

This unifies the subtitle rendering path. Now all subtitle rendering
goes through sd_ass.c/sd_lavc.c/sd_spu.c.

Before that commit, the spudec.h functions were used directly in
mplayer.c, which introduced many special cases. Add sd_spu.c, which is
just a small wrapper connecting the new subtitle render API with the
dusty old vobsub decoder in spudec.c.

One detail that changes is that we always pass the palette as extra
data, instead of passing the libdvdread palette as pointer to spudec
directly. This is a bit roundabout, but actually makes the code simpler
and more elegant: the difference between DVD and non-DVD dvdsubs is
reduced.

Ideally, we would just delete spudec.c and use libavcodec's DVD sub
decoder. However, DVD playback with demux_mpg produces packets
incompatible to lavc. There are incompatibilities the other way around
as well: packets from libavformat's vobsub demuxer are incompatible to
spudec.c. So we define a new subtitle codec name for demux_mpg subs,
"dvd_subtitle_mpg", which only sd_spu can decode.

There is actually code in spudec.c to "assemble" fragments into complete
packets, but using the whole spudec.c is easier than trying to move this
code into demux_mpg to fix subtitle packets.

As additional complication, Libav 9.x can't decode DVD subs correctly,
so use sd_spu in that case as well.
This commit is contained in:
wm4 2013-04-29 01:13:22 +02:00
parent 724f576211
commit 2684280643
13 changed files with 171 additions and 179 deletions

View File

@ -230,6 +230,7 @@ SOURCES = talloc.c \
sub/find_subfiles.c \
sub/img_convert.c \
sub/sd_lavc.c \
sub/sd_spu.c \
sub/spudec.c \
sub/sub.c \
sub/subassconvert.c \

View File

@ -50,7 +50,6 @@
#include "audio/filter/af.h"
#include "video/decode/dec_video.h"
#include "audio/decode/dec_audio.h"
#include "sub/spudec.h"
#include "core/path.h"
#include "sub/ass_mp.h"
#include "stream/tv.h"
@ -1307,8 +1306,7 @@ static int mp_property_sub_visibility(m_option_t *prop, int action,
switch (action) {
case M_PROPERTY_SET:
opts->sub_visibility = *(int *)arg;
if (vo_spudec)
vo_osd_changed(OSDTYPE_SPU);
osd_changed_all(mpctx->osd);
return M_PROPERTY_OK;
case M_PROPERTY_GET:
*(int *)arg = opts->sub_visibility;
@ -1317,23 +1315,6 @@ static int mp_property_sub_visibility(m_option_t *prop, int action,
return M_PROPERTY_NOT_IMPLEMENTED;
}
/// Show only forced subtitles (RW)
static int mp_property_sub_forced_only(m_option_t *prop, int action,
void *arg, MPContext *mpctx)
{
struct MPOpts *opts = &mpctx->opts;
if (!vo_spudec)
return M_PROPERTY_UNAVAILABLE;
if (action == M_PROPERTY_SET) {
opts->forced_subs_only = *(int *)arg;
spudec_set_forced_subs_only(vo_spudec, opts->forced_subs_only);
return M_PROPERTY_OK;
}
return mp_property_generic_option(prop, action, arg, mpctx);
}
#ifdef CONFIG_TV
@ -1514,7 +1495,7 @@ static const m_option_t mp_properties[] = {
M_OPTION_PROPERTY_CUSTOM("sub-pos", mp_property_sub_pos),
{ "sub-visibility", mp_property_sub_visibility, CONF_TYPE_FLAG,
M_OPT_RANGE, 0, 1, NULL },
M_OPTION_PROPERTY_CUSTOM("sub-forced-only", mp_property_sub_forced_only),
M_OPTION_PROPERTY_CUSTOM("sub-forced-only", property_osd_helper),
M_OPTION_PROPERTY_CUSTOM("sub-scale", property_osd_helper),
#ifdef CONFIG_ASS
M_OPTION_PROPERTY_CUSTOM("ass-use-margins", property_osd_helper),

View File

@ -31,7 +31,6 @@
#define INITIALIZED_AO 2
#define INITIALIZED_VOL 4
#define INITIALIZED_GETCH2 8
#define INITIALIZED_SPUDEC 32
#define INITIALIZED_STREAM 64
#define INITIALIZED_DEMUXER 512
#define INITIALIZED_ACODEC 1024
@ -293,7 +292,6 @@ extern int forced_subs_only;
void uninit_player(struct MPContext *mpctx, unsigned int mask);
void reinit_audio_chain(struct MPContext *mpctx);
void init_vo_spudec(struct MPContext *mpctx);
double playing_audio_pts(struct MPContext *mpctx);
struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename,
float fps, int noerr);

View File

@ -95,8 +95,6 @@
#include "core/codecs.h"
#include "sub/spudec.h"
#include "osdep/getch2.h"
#include "osdep/timer.h"
@ -554,12 +552,6 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask)
getch2_disable();
}
if (mask & INITIALIZED_SPUDEC) {
mpctx->initialized_flags &= ~INITIALIZED_SPUDEC;
spudec_free(vo_spudec);
vo_spudec = NULL;
}
if (mask & INITIALIZED_VOL) {
mpctx->initialized_flags &= ~INITIALIZED_VOL;
if (mpctx->mixer.ao) {
@ -1104,38 +1096,6 @@ struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename,
return track;
}
void init_vo_spudec(struct MPContext *mpctx)
{
uninit_player(mpctx, INITIALIZED_SPUDEC);
unsigned width, height;
// we currently can't work without video stream
if (!mpctx->sh_video)
return;
width = mpctx->sh_video->disp_w;
height = mpctx->sh_video->disp_h;
#ifdef CONFIG_DVDREAD
if (vo_spudec == NULL && mpctx->stream->type == STREAMTYPE_DVD) {
vo_spudec = spudec_new_scaled(((dvd_priv_t *)(mpctx->stream->priv))->
cur_pgc->palette, width, height, NULL, 0);
}
#endif
if (vo_spudec == NULL && mpctx->sh_sub) {
sh_sub_t *sh = mpctx->sh_sub;
vo_spudec = spudec_new_scaled(NULL, width, height, sh->extradata,
sh->extradata_len);
}
if (vo_spudec != NULL) {
mpctx->initialized_flags |= INITIALIZED_SPUDEC;
mp_property_do("sub-forced-only", M_PROPERTY_SET,
&mpctx->opts.forced_subs_only, mpctx);
}
}
int mp_get_cache_percent(struct MPContext *mpctx)
{
if (mpctx->stream) {
@ -1867,10 +1827,6 @@ static void reset_subtitles(struct MPContext *mpctx)
if (mpctx->sh_sub)
sub_reset(mpctx->sh_sub, mpctx->osd);
set_osd_subtitle(mpctx, NULL);
if (vo_spudec) {
spudec_reset(vo_spudec);
vo_osd_changed(OSDTYPE_SPU);
}
}
static void update_subtitles(struct MPContext *mpctx, double refpts_tl)
@ -1892,46 +1848,7 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl)
double curpts_s = refpts_tl - mpctx->osd->sub_offset;
double refpts_s = refpts_tl - video_offset;
// DVD sub:
if (is_dvd_sub(type) && !(sh_sub && sh_sub->active)) {
int timestamp;
// Get a sub packet from the demuxer (or the vobsub.c thing, which
// should be a demuxer, but isn't).
while (1) {
// Vobsub
len = 0;
{
// DVD sub
assert(d_sub->sh == sh_sub);
len = ds_get_packet_sub(d_sub, (unsigned char **)&packet);
if (len > 0) {
// XXX This is wrong, sh_video->pts can be arbitrarily
// much behind demuxing position. Unfortunately using
// d_video->pts which would have been the simplest
// improvement doesn't work because mpeg specific hacks
// in video.c set d_video->pts to 0.
float x = d_sub->pts - refpts_s;
if (x > -20 && x < 20) // prevent missing subs on pts reset
timestamp = 90000 * d_sub->pts;
else
timestamp = 90000 * curpts_s;
mp_dbg(MSGT_CPLAYER, MSGL_V, "\rDVD sub: len=%d "
"v_pts=%5.3f s_pts=%5.3f ts=%d \n", len,
refpts_s, d_sub->pts, timestamp);
}
}
if (len <= 0 || !packet)
break;
// create it only here, since with some broken demuxers we might
// type = v but no DVD sub and we currently do not change the
// "original frame size" ever after init, leading to wrong-sized
// PGS subtitles.
if (!vo_spudec)
vo_spudec = spudec_new(NULL);
if (timestamp >= 0)
spudec_assemble(vo_spudec, packet, len, timestamp);
}
} else if (d_sub && sh_sub && sh_sub->active) {
if (d_sub && sh_sub && sh_sub->active) {
bool non_interleaved = is_non_interleaved(mpctx, track);
if (non_interleaved)
ds_get_next_pts(d_sub);
@ -1979,11 +1896,6 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl)
ds_get_next_pts(d_sub);
}
}
if (vo_spudec) {
spudec_heartbeat(vo_spudec, 90000 * curpts_s);
if (spudec_changed(vo_spudec))
vo_osd_changed(OSDTYPE_SPU);
}
if (!mpctx->osd->render_bitmap_subs)
set_osd_subtitle(mpctx, sub_get_text(mpctx->osd, curpts_s));
@ -2030,10 +1942,10 @@ static double timing_sleep(struct MPContext *mpctx, double time_frame)
}
static void set_dvdsub_fake_extradata(struct sh_sub *sh_sub, struct stream *st,
struct sh_video *sh_video)
int width, int height)
{
#ifdef CONFIG_DVDREAD
if (st->type != STREAMTYPE_DVD || !sh_video)
if (st->type != STREAMTYPE_DVD)
return;
struct mp_csp_params csp = MP_CSP_PARAMS_DEFAULTS;
@ -2042,10 +1954,13 @@ static void set_dvdsub_fake_extradata(struct sh_sub *sh_sub, struct stream *st,
float cmatrix[3][4];
mp_get_yuv2rgb_coeffs(&csp, cmatrix);
int width = sh_video->disp_w;
int height = sh_video->disp_h;
int *palette = ((dvd_priv_t *)st->priv)->cur_pgc->palette;
if (width == 0 || height == 0) {
width = 720;
height = 480;
}
char *s = NULL;
s = talloc_asprintf_append(s, "size: %dx%d\n", width, height);
s = talloc_asprintf_append(s, "palette: ");
@ -2072,6 +1987,7 @@ static void reinit_subs(struct MPContext *mpctx)
{
struct MPOpts *opts = &mpctx->opts;
struct track *track = mpctx->current_track[STREAM_SUB];
struct osd_state *osd = mpctx->osd;
assert(!(mpctx->initialized_flags & INITIALIZED_SUB));
@ -2098,24 +2014,17 @@ static void reinit_subs(struct MPContext *mpctx)
mpctx->initialized_flags |= INITIALIZED_SUB;
osd->sub_video_w = mpctx->sh_video ? mpctx->sh_video->disp_w : 0;
osd->sub_video_h = mpctx->sh_video ? mpctx->sh_video->disp_h : 0;
if (track->sh_sub) {
sub_init(track->sh_sub, mpctx->osd);
sub_init(track->sh_sub, osd);
} else if (track->stream) {
struct stream *s = track->demuxer ? track->demuxer->stream : NULL;
if (s && s->type == STREAMTYPE_DVD)
set_dvdsub_fake_extradata(mpctx->sh_sub, s, mpctx->sh_video);
// lavc dvdsubdec doesn't read color/resolution on Libav 9.1 and below
// Don't use it for new ffmpeg; spudec can't handle ffmpeg .idx demuxing
// (ffmpeg added .idx demuxing during lavc 54.79.100)
bool broken_lavc = false;
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 40, 0)
broken_lavc = true;
#endif
if (is_dvd_sub(mpctx->sh_sub->gsh->codec) && track->demuxer
&& (track->demuxer->type == DEMUXER_TYPE_MPEG_PS || broken_lavc))
init_vo_spudec(mpctx);
else
sub_init(mpctx->sh_sub, mpctx->osd);
if (track->demuxer && track->demuxer->stream) {
set_dvdsub_fake_extradata(mpctx->sh_sub, track->demuxer->stream,
osd->sub_video_w, osd->sub_video_h);
}
sub_init(mpctx->sh_sub, osd);
}
// Decides whether to use OSD path or normal subtitle rendering path.

View File

@ -523,7 +523,7 @@ static int demux_mpg_read_packet(demuxer_t *demux,int id){
if(!demux->s_streams[aid]){
sh_sub_t *sh = new_sh_sub(demux, aid);
if (sh) sh->gsh->codec = "dvd_subtitle";
if (sh) sh->gsh->codec = "dvd_subtitle_mpg";
mp_msg(MSGT_DEMUX,MSGL_V,"==> Found subtitle: %d\n",aid);
}

View File

@ -29,6 +29,16 @@
extern const struct sd_functions sd_ass;
extern const struct sd_functions sd_lavc;
extern const struct sd_functions sd_spu;
static const struct sd_functions *sd_list[] = {
#ifdef CONFIG_ASS
&sd_ass,
#endif
&sd_lavc,
&sd_spu,
NULL
};
bool is_text_sub(const char *t)
{
@ -46,20 +56,20 @@ bool is_ass_sub(const char *t)
bool is_dvd_sub(const char *t)
{
return t && strcmp(t, "dvd_subtitle") == 0;
return t && (strcmp(t, "dvd_subtitle") == 0 ||
strcmp(t, "dvd_subtitle_mpg") == 0);
}
void sub_init(struct sh_sub *sh, struct osd_state *osd)
{
const char *format = sh->gsh->codec;
sh->sd_driver = NULL;
for (int n = 0; sd_list[n]; n++) {
if (sd_list[n]->supports_format(sh->gsh->codec)) {
sh->sd_driver = sd_list[n];
break;
}
}
assert(!osd->sh_sub);
if (sd_lavc.supports_format(format))
sh->sd_driver = &sd_lavc;
#ifdef CONFIG_ASS
if (sd_ass.supports_format(format))
sh->sd_driver = &sd_ass;
#endif
if (sh->sd_driver) {
if (sh->sd_driver->init(sh, osd) < 0)
return;

View File

@ -295,7 +295,6 @@ struct sh_sub *sd_ass_create_from_track(struct ass_track *track,
.gsh = talloc_struct(sh, struct sh_stream, {
.codec = codec,
}),
.sd_driver = &sd_ass,
.context = talloc_struct(sh, struct sd_ass_priv, {
.ass_track = track,
.vsfilter_aspect = is_ass_sub(codec),

View File

@ -43,6 +43,14 @@ struct sd_lavc_priv {
static bool supports_format(const char *format)
{
// lavc dvdsubdec doesn't read color/resolution on Libav 9.1 and below,
// so fall back to sd_spu in this case. Never use sd_spu with new ffmpeg;
// spudec can't handle ffmpeg .idx demuxing (added to lavc in 54.79.100).
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 40, 0)
if (is_dvd_sub(format))
return false;
#endif
enum AVCodecID cid = mp_codec_to_av_codec_id(format);
// Supported codecs must be known to decode to paletted bitmaps
switch (cid) {

99
sub/sd_spu.c Normal file
View File

@ -0,0 +1,99 @@
/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <assert.h>
#include "talloc.h"
#include "core/options.h"
#include "demux/stheader.h"
#include "sd.h"
#include "sub.h"
#include "spudec.h"
struct sd_spu_priv {
void *spudec;
};
static bool supports_format(const char *format)
{
return is_dvd_sub(format);
}
static int init(struct sh_sub *sh, struct osd_state *osd)
{
if (sh->initialized)
return 0;
void *spudec = spudec_new_scaled(osd->sub_video_w, osd->sub_video_h,
sh->extradata, sh->extradata_len);
if (!spudec)
return -1;
struct sd_spu_priv *priv = talloc_zero(NULL, struct sd_spu_priv);
priv->spudec = spudec;
sh->context = priv;
return 0;
}
static void decode(struct sh_sub *sh, struct osd_state *osd, void *data,
int data_len, double pts, double duration)
{
struct sd_spu_priv *priv = sh->context;
if (pts < 0 || data_len == 0)
return;
spudec_assemble(priv->spudec, data, data_len, pts * 90000);
}
static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd,
struct mp_osd_res d, double pts,
struct sub_bitmaps *res)
{
struct MPOpts *opts = sh->opts;
struct sd_spu_priv *priv = sh->context;
spudec_set_forced_subs_only(priv->spudec, opts->forced_subs_only);
spudec_heartbeat(priv->spudec, pts * 90000);
if (spudec_visible(priv->spudec))
spudec_get_indexed(priv->spudec, &d, res);
}
static void reset(struct sh_sub *sh, struct osd_state *osd)
{
struct sd_spu_priv *priv = sh->context;
spudec_reset(priv->spudec);
}
static void uninit(struct sh_sub *sh)
{
struct sd_spu_priv *priv = sh->context;
spudec_free(priv->spudec);
talloc_free(priv);
}
const struct sd_functions sd_spu = {
.supports_format = supports_format,
.init = init,
.decode = decode,
.get_bitmaps = get_bitmaps,
.reset = reset,
.switch_off = reset,
.uninit = uninit,
};

View File

@ -575,6 +575,13 @@ void spudec_assemble(void *this, unsigned char *packet, unsigned int len, int pt
#endif
}
void spudec_set_changed(void *this)
{
spudec_handle_t *spu = this;
spu->spu_changed = 1;
}
void spudec_reset(void *this) // called after seek
{
spudec_handle_t *spu = this;
@ -583,6 +590,7 @@ void spudec_reset(void *this) // called after seek
spu->now_pts = 0;
spu->end_pts = 0;
spu->packet_size = spu->packet_offset = 0;
spudec_set_changed(spu);
}
void spudec_heartbeat(void *this, unsigned int pts100)
@ -611,7 +619,7 @@ void spudec_heartbeat(void *this, unsigned int pts100)
spudec_process_data(spu, packet);
}
spudec_free_packet(packet);
spu->spu_changed = 1;
spudec_set_changed(spu);
}
}
@ -626,10 +634,11 @@ int spudec_visible(void *this){
void spudec_set_forced_subs_only(void * const this, const unsigned int flag)
{
if(this){
((spudec_handle_t *)this)->forced_subs_only=flag;
mp_msg(MSGT_SPUDEC,MSGL_DBG2,"SPU: Display only forced subs now %s\n", flag ? "enabled": "disabled");
}
spudec_handle_t *spu = this;
if (!!flag != !!spu->forced_subs_only) {
spu->forced_subs_only = !!flag;
spudec_set_changed(spu);
}
}
void spudec_get_indexed(void *this, struct mp_osd_res *dim,
@ -738,17 +747,14 @@ static void spudec_parse_extradata(spudec_handle_t *this,
free(buffer);
}
void *spudec_new_scaled(unsigned int *palette, unsigned int frame_width, unsigned int frame_height, uint8_t *extradata, int extradata_len)
void *spudec_new_scaled(unsigned int frame_width, unsigned int frame_height, uint8_t *extradata, int extradata_len)
{
spudec_handle_t *this = calloc(1, sizeof(spudec_handle_t));
if (this){
this->orig_frame_height = frame_height;
this->orig_frame_width = frame_width;
// set up palette:
if (palette)
memcpy(this->global_palette, palette, sizeof(this->global_palette));
else
this->auto_palette = 1;
this->auto_palette = 1;
if (extradata)
spudec_parse_extradata(this, extradata, extradata_len);
/* XXX Although the video frame is some size, the SPU frame is
@ -768,11 +774,6 @@ void *spudec_new_scaled(unsigned int *palette, unsigned int frame_width, unsigne
return this;
}
void *spudec_new(unsigned int *palette)
{
return spudec_new_scaled(palette, 0, 0, NULL, 0);
}
void spudec_free(void *this)
{
spudec_handle_t *spu = this;

View File

@ -27,12 +27,12 @@ struct mp_osd_res;
void spudec_heartbeat(void *this, unsigned int pts100);
void spudec_assemble(void *this, unsigned char *packet, unsigned int len, int pts100);
void spudec_get_indexed(void *this, struct mp_osd_res *dim, struct sub_bitmaps *res);
void *spudec_new_scaled(unsigned int *palette, unsigned int frame_width, unsigned int frame_height, uint8_t *extradata, int extradata_len);
void *spudec_new(unsigned int *palette);
void *spudec_new_scaled(unsigned int frame_width, unsigned int frame_height, uint8_t *extradata, int extradata_len);
void spudec_free(void *this);
void spudec_reset(void *this); // called after seek
int spudec_visible(void *this); // check if spu is visible
int spudec_changed(void *this);
void spudec_set_changed(void *this);
void spudec_set_forced_subs_only(void * const this, const unsigned int flag);
#endif /* MPLAYER_SPUDEC_H */

View File

@ -37,7 +37,6 @@
#include "dec_sub.h"
#include "img_convert.h"
#include "draw_bmp.h"
#include "spudec.h"
#include "subreader.h"
#include "video/mp_image.h"
#include "video/mp_image_pool.h"
@ -48,9 +47,6 @@ int sub_visibility=1;
float sub_delay = 0;
float sub_fps = 0;
void *vo_spudec=NULL;
void *vo_vobsub=NULL;
static const struct osd_style_opts osd_style_opts_def = {
.font = "Sans",
.font_size = 45,
@ -114,7 +110,6 @@ struct osd_state *osd_create(struct MPOpts *opts, struct ass_library *asslib)
osd->objs[n] = obj;
}
osd->objs[OSDTYPE_SPU]->is_sub = true; // spudec.c
osd->objs[OSDTYPE_SUB]->is_sub = true; // dec_sub.c
osd->objs[OSDTYPE_SUBTEXT]->is_sub = true; // osd_libass.c
@ -155,12 +150,6 @@ void osd_set_sub(struct osd_state *osd, const char *text)
vo_osd_changed(OSDTYPE_SUBTEXT);
}
static bool spu_visible(struct osd_state *osd, struct osd_object *obj)
{
struct MPOpts *opts = osd->opts;
return opts->sub_visibility && vo_spudec && spudec_visible(vo_spudec);
}
static void render_object(struct osd_state *osd, struct osd_object *obj,
struct mp_osd_res res, double video_pts,
const bool sub_formats[SUBBITMAP_COUNT],
@ -179,10 +168,7 @@ static void render_object(struct osd_state *osd, struct osd_object *obj,
obj->force_redraw = true;
obj->vo_res = res;
if (obj->type == OSDTYPE_SPU) {
if (spu_visible(osd, obj))
spudec_get_indexed(vo_spudec, &obj->vo_res, out_imgs);
} else if (obj->type == OSDTYPE_SUB) {
if (obj->type == OSDTYPE_SUB) {
if (osd->render_bitmap_subs) {
double sub_pts = video_pts;
if (sub_pts != MP_NOPTS_VALUE)

View File

@ -86,7 +86,6 @@ struct mp_osd_res {
enum mp_osdtype {
OSDTYPE_SUB,
OSDTYPE_SUBTEXT,
OSDTYPE_SPU,
OSDTYPE_PROGBAR,
OSDTYPE_OSD,
@ -144,6 +143,10 @@ struct osd_state {
struct MPOpts *opts;
// Video resolution used for subtitle decoding. Doesn't necessarily match
// the resolution of the VO, nor does it have to be the OSD resolution.
int sub_video_w, sub_video_h;
// Internal to sub.c
struct mp_draw_sub_cache *draw_cache;
@ -152,9 +155,6 @@ struct osd_state {
struct ass_library *osd_ass_library;
};
extern void* vo_spudec;
extern void* vo_vobsub;
// Start of OSD symbols in osd_font.pfb
#define OSD_CODEPOINTS 0xE000