summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2013-03-30 20:08:56 +0100
committerwm4 <wm4@nowhere>2013-03-30 20:23:45 +0100
commitef3c0e6edafd6460d30c24dfd9f5b2f50401f38f (patch)
tree5b6b085430e3f200bfb033ca721c996e1d092c37
parentd39b131bde2f90c1607e1d9292c1a4ce291eb148 (diff)
downloadmpv-ef3c0e6edafd6460d30c24dfd9f5b2f50401f38f.tar.bz2
mpv-ef3c0e6edafd6460d30c24dfd9f5b2f50401f38f.tar.xz
osd: draw the OSD bar with ASS vector drawings
Drawing the bar with vector drawings (instead with characters from the OSD font) offers more flexibility and looks better. This also adds chapter marks to the OSD bar, which are visible as small triangles on the top and bottom inner border of the bar. Change the default position of the OSD bar below the center of the screen. This is less annoying than putting the bar directly into the center of the view, where it obscures the video. The new position is not quite on the bottom of the screen to avoid collisions with subtitles. The old centered position can be forced with ``--osd-bar-align-y=0``. Also make it possible to change the OSD bar width/height with the new --osd-bar-w and --osd-bar-h options. It's possible that the new OSD bar renders much slower than the old one. There are two reasons for this: 1. the character based bar allowed libass to cache each character, while the vector drawing forces it to redraw every time the bar position changes. 2., the bar position is updated at a much higher granularity (the bar position is passed along as float instead of as integer in the range 0-100, so the bar will be updated on every single video frame).
-rw-r--r--DOCS/man/en/options.rst11
-rw-r--r--core/cfg-mplayer.h2
-rw-r--r--core/defaultopts.c3
-rw-r--r--core/mplayer.c26
-rw-r--r--core/options.h2
-rw-r--r--sub/osd_libass.c230
-rw-r--r--sub/sub.h9
7 files changed, 212 insertions, 71 deletions
diff --git a/DOCS/man/en/options.rst b/DOCS/man/en/options.rst
index 91078b2424..98019c0333 100644
--- a/DOCS/man/en/options.rst
+++ b/DOCS/man/en/options.rst
@@ -1315,12 +1315,19 @@
prefixes, see ``Input command prefixes``. If you want to disable the OSD
completely, use ``--osd-level=0``.
---osd-bar-align-x=<-1..1>
+--osd-bar-align-x=<-1-1>
Position of the OSD bar. -1 is far left, 0 is centered, 1 is far right.
---osd-bar-align-y=<-1..1>
+--osd-bar-align-y=<-1-1>
Position of the OSD bar. -1 is top, 0 is centered, 1 is bottom.
+--osd-bar-w=<1-100>
+ Width of the OSD bar, in percentage of the screen width (default: 75).
+ A value of 0.5 means the bar is half the screen wide.
+
+--osd-bar-h=<0.1-50>
+ Height of the OSD bar, in percentage of the screen height (default: 3.125).
+
--osd-back-color=<#RRGGBB>, --sub-text-back-color=<#RRGGBB>
See ``--osd-color``. Color used for OSD/sub text background.
diff --git a/core/cfg-mplayer.h b/core/cfg-mplayer.h
index 3b164a7346..5bae70ab0e 100644
--- a/core/cfg-mplayer.h
+++ b/core/cfg-mplayer.h
@@ -501,6 +501,8 @@ const m_option_t common_opts[] = {
OPT_FLAG("osd-bar", osd_bar_visible, 0),
OPT_FLOATRANGE("osd-bar-align-x", osd_bar_align_x, 0, -1.0, +1.0),
OPT_FLOATRANGE("osd-bar-align-y", osd_bar_align_y, 0, -1.0, +1.0),
+ OPT_FLOATRANGE("osd-bar-w", osd_bar_w, 0, 1, 100),
+ OPT_FLOATRANGE("osd-bar-h", osd_bar_h, 0, 0.1, 50),
OPT_SUBSTRUCT("osd", osd_style, osd_style_conf, 0),
OPT_SUBSTRUCT("sub-text", sub_text_style, osd_style_conf, 0),
{NULL, NULL, 0, 0, 0, 0, NULL}
diff --git a/core/defaultopts.c b/core/defaultopts.c
index 9f544d6d55..36170df725 100644
--- a/core/defaultopts.c
+++ b/core/defaultopts.c
@@ -43,6 +43,9 @@ void set_default_mplayer_options(struct MPOpts *opts)
.gamma_hue = 1000,
.osd_level = 1,
.osd_duration = 1000,
+ .osd_bar_align_y = 0.5,
+ .osd_bar_w = 75.0,
+ .osd_bar_h = 3.125,
.loop_times = -1,
.ordered_chapters = 1,
.chapter_merge_threshold = 100,
diff --git a/core/mplayer.c b/core/mplayer.c
index eccb40b7ec..b0563a7c2c 100644
--- a/core/mplayer.c
+++ b/core/mplayer.c
@@ -1330,7 +1330,8 @@ void set_osd_bar(struct MPContext *mpctx, int type, const char *name,
if (mpctx->sh_video && opts->term_osd != 1) {
mpctx->osd_visible = (GetTimerMS() + opts->osd_duration) | 1;
mpctx->osd->progbar_type = type;
- mpctx->osd->progbar_value = 256 * (val - min) / (max - min);
+ mpctx->osd->progbar_value = (val - min) / (max - min);
+ mpctx->osd->progbar_num_stops = 0;
vo_osd_changed(OSDTYPE_PROGBAR);
return;
}
@@ -1345,7 +1346,7 @@ static void update_osd_bar(struct MPContext *mpctx, int type,
double min, double max, double val)
{
if (mpctx->osd->progbar_type == type) {
- int new_value = 256 * (val - min) / (max - min);
+ float new_value = (val - min) / (max - min);
if (new_value != mpctx->osd->progbar_value) {
mpctx->osd->progbar_value = new_value;
vo_osd_changed(OSDTYPE_PROGBAR);
@@ -1353,6 +1354,26 @@ static void update_osd_bar(struct MPContext *mpctx, int type,
}
}
+static void set_osd_bar_chapters(struct MPContext *mpctx, int type)
+{
+ struct osd_state *osd = mpctx->osd;
+ osd->progbar_num_stops = 0;
+ if (osd->progbar_type == type) {
+ double len = get_time_length(mpctx);
+ if (len > 0) {
+ int num = get_chapter_count(mpctx);
+ for (int n = 0; n < num; n++) {
+ double time = chapter_start_time(mpctx, n);
+ if (time >= 0) {
+ float pos = time / len;
+ MP_TARRAY_APPEND(osd, osd->progbar_stops,
+ osd->progbar_num_stops, pos);
+ }
+ }
+ }
+ }
+}
+
void set_osd_function(struct MPContext *mpctx, int osd_function)
{
struct MPOpts *opts = &mpctx->opts;
@@ -1432,6 +1453,7 @@ static void add_seek_osd_messages(struct MPContext *mpctx)
if (mpctx->add_osd_seek_info & OSD_SEEK_INFO_BAR) {
set_osd_bar(mpctx, OSD_BAR_SEEK, "Position", 0, 1,
av_clipf(get_current_pos_ratio(mpctx), 0, 1));
+ set_osd_bar_chapters(mpctx, OSD_BAR_SEEK);
}
if (mpctx->add_osd_seek_info & OSD_SEEK_INFO_TEXT) {
mp_osd_msg_t *msg = add_osd_msg(mpctx, OSD_MSG_TEXT, 1,
diff --git a/core/options.h b/core/options.h
index 4483d6bed2..4291f2d872 100644
--- a/core/options.h
+++ b/core/options.h
@@ -161,6 +161,8 @@ typedef struct MPOpts {
int osd_bar_visible;
float osd_bar_align_x;
float osd_bar_align_y;
+ float osd_bar_w;
+ float osd_bar_h;
struct osd_style_opts *osd_style;
struct osd_style_opts *sub_text_style;
float sub_scale;
diff --git a/sub/osd_libass.c b/sub/osd_libass.c
index bbd925f135..9f5f1fd692 100644
--- a/sub/osd_libass.c
+++ b/sub/osd_libass.c
@@ -61,14 +61,19 @@ void osd_destroy_backend(struct osd_state *osd)
osd->osd_ass_library = NULL;
}
-static ASS_Track *create_osd_ass_track(struct osd_state *osd)
+static void create_osd_ass_track(struct osd_state *osd, struct osd_object *obj)
{
- ASS_Track *track = ass_new_track(osd->osd_ass_library);
+ ASS_Track *track = obj->osd_track;
+ if (!track)
+ track = ass_new_track(osd->osd_ass_library);
+
+ double aspect = 1.0 * obj->vo_res.w / FFMAX(obj->vo_res.h, 1) /
+ obj->vo_res.display_par;
track->track_type = TRACK_TYPE_ASS;
track->Timer = 100.;
track->PlayResY = MP_ASS_FONT_PLAYRESY;
- track->PlayResX = track->PlayResY * 1.33333;
+ track->PlayResX = track->PlayResY * aspect;
track->WrapStyle = 1; // end-of-line wrapping instead of smart wrapping
if (track->n_styles == 0) {
@@ -83,18 +88,19 @@ static ASS_Track *create_osd_ass_track(struct osd_state *osd)
style->Encoding = -1;
}
- return track;
+ obj->osd_track = track;
}
-static ASS_Event *get_osd_ass_event(ASS_Track *track)
+static ASS_Event *add_osd_ass_event(ASS_Track *track, const char *text)
{
- ass_flush_events(track);
- ass_alloc_event(track);
- ASS_Event *event = track->events + 0;
+ int n = ass_alloc_event(track);
+ ASS_Event *event = track->events + n;
event->Start = 0;
event->Duration = 100;
event->Style = track->default_style;
assert(event->Text == NULL);
+ if (text)
+ event->Text = strdup(text);
return event;
}
@@ -135,67 +141,135 @@ static char *mangle_ass(const char *in)
static void update_osd(struct osd_state *osd, struct osd_object *obj)
{
- if (!osd->osd_text[0]) {
- clear_obj(obj);
+ create_osd_ass_track(osd, obj);
+ clear_obj(obj);
+ if (!osd->osd_text[0])
return;
- }
- if (!obj->osd_track)
- obj->osd_track = create_osd_ass_track(osd);
- ASS_Event *event = get_osd_ass_event(obj->osd_track);
char *text = mangle_ass(osd->osd_text);
- event->Text = strdup(text);
+ add_osd_ass_event(obj->osd_track, text);
talloc_free(text);
}
-static int get_align(float val, int res, int *out_margin)
+// align: -1 .. +1
+// frame: size of the containing area
+// obj: size of the object that should be positioned inside the area
+// margin: min. distance from object to frame (as long as -1 <= align <= +1)
+static float get_align(float align, float frame, float obj, float margin)
{
- *out_margin = FFMAX(0, (1.0 - fabs(val)) * res / 2);
- if (fabs(val) < 0.1)
- return 1; // centered
- return val > 0 ? 2 : 0; // bottom / top (or right / left)
+ frame -= margin * 2;
+ return margin + frame / 2 - obj / 2 + (frame - obj) / 2 * align;
}
-static const int ass_align_x[3] = {1, 2, 3};
-static const int ass_align_y[3] = {4, 8, 0};
+struct ass_draw {
+ int scale;
+ char *text;
+};
-#define OSDBAR_ELEMS 46
+static void ass_draw_start(struct ass_draw *d)
+{
+ d->scale = FFMAX(d->scale, 1);
+ d->text = talloc_asprintf_append(d->text, "{\\p%d}", d->scale);
+}
-static void update_progbar(struct osd_state *osd, struct osd_object *obj)
+static void ass_draw_stop(struct ass_draw *d)
+{
+ d->text = talloc_strdup_append(d->text, "{\\p0}");
+}
+
+static void ass_draw_c(struct ass_draw *d, float x, float y)
+{
+ int ix = round(x * (1 << (d->scale - 1)));
+ int iy = round(y * (1 << (d->scale - 1)));
+ d->text = talloc_asprintf_append(d->text, " %d %d", ix, iy);
+}
+
+static void ass_draw_append(struct ass_draw *d, const char *t)
+{
+ d->text = talloc_strdup_append(d->text, t);
+}
+
+static void ass_draw_move_to(struct ass_draw *d, float x, float y)
+{
+ ass_draw_append(d, " m");
+ ass_draw_c(d, x, y);
+}
+
+static void ass_draw_line_to(struct ass_draw *d, float x, float y)
+{
+ ass_draw_append(d, " l");
+ ass_draw_c(d, x, y);
+}
+
+static void ass_draw_rect_ccw(struct ass_draw *d, float x0, float y0,
+ float x1, float y1)
+{
+ ass_draw_move_to(d, x0, y0);
+ ass_draw_line_to(d, x0, y1);
+ ass_draw_line_to(d, x1, y1);
+ ass_draw_line_to(d, x1, y0);
+}
+
+static void ass_draw_rect_cw(struct ass_draw *d, float x0, float y0,
+ float x1, float y1)
+{
+ ass_draw_move_to(d, x0, y0);
+ ass_draw_line_to(d, x1, y0);
+ ass_draw_line_to(d, x1, y1);
+ ass_draw_line_to(d, x0, y1);
+}
+
+static void ass_draw_reset(struct ass_draw *d)
+{
+ talloc_free(d->text);
+ d->text = NULL;
+}
+
+static void get_osd_bar_box(struct osd_state *osd, struct osd_object *obj,
+ float *o_x, float *o_y, float *o_w, float *o_h,
+ float *o_border)
{
struct MPOpts *opts = osd->opts;
- if (osd->progbar_type < 0) {
- clear_obj(obj);
- return;
+ bool new_track = !obj->osd_track;
+ create_osd_ass_track(osd, obj);
+ ASS_Track *track = obj->osd_track;
+ ASS_Style *style = track->styles + track->default_style;
+
+ *o_w = track->PlayResX * (opts->osd_bar_w / 100.0);
+ *o_h = track->PlayResY * (opts->osd_bar_h / 100.0);
+
+ if (new_track) {
+ float base_size = 0.03125;
+ style->Outline *= *o_h / track->PlayResY / base_size;
+ // So that the chapter marks have space between them
+ style->Outline = FFMIN(style->Outline, *o_h / 5.2);
+ // So that the border is not 0
+ style->Outline = FFMAX(style->Outline, *o_h / 32.0);
+ // Rendering with shadow is broken (because there's more than one shape)
+ style->Shadow = 0;
}
- if (!obj->osd_track)
- obj->osd_track = create_osd_ass_track(osd);
+ *o_border = style->Outline;
- ASS_Style *style = obj->osd_track->styles + obj->osd_track->default_style;
+ *o_x = get_align(opts->osd_bar_align_x, track->PlayResX, *o_w, *o_border);
+ *o_y = get_align(opts->osd_bar_align_y, track->PlayResY, *o_h, *o_border);
+}
+
+static void update_progbar(struct osd_state *osd, struct osd_object *obj)
+{
+ float px, py, width, height, border;
+ get_osd_bar_box(osd, obj, &px, &py, &width, &height, &border);
- int ax = get_align(opts->osd_bar_align_x, obj->osd_track->PlayResX,
- &style->MarginR);
- int ay = get_align(opts->osd_bar_align_y, obj->osd_track->PlayResY,
- &style->MarginV);
- style->Alignment = ass_align_x[ax] + ass_align_y[ay];
- style->MarginL = style->MarginR;
+ clear_obj(obj);
- // We need a fixed font size with respect to the OSD width.
- // Assume the OSD bar takes 2/3 of the OSD width at PlayResY=288 and
- // FontSize=22 with an OSD aspect ratio of 16:9. Rescale as needed.
- // xxx can fail when unknown fonts are involved
- double asp = (double)obj->vo_res.w / obj->vo_res.h;
- double scale = (asp / 1.77777) * (obj->osd_track->PlayResY / 288.0);
- style->ScaleX = style->ScaleY = scale;
- style->FontSize = 22.0;
- style->Outline = style->FontSize / 16 * scale;
+ if (osd->progbar_type < 0)
+ return;
- int active = (osd->progbar_value * OSDBAR_ELEMS + 255) / 256;
- active = FFMIN(OSDBAR_ELEMS, FFMAX(active, 0));
+ float sx = px - border * 2 - height / 4; // includes additional spacing
+ float sy = py + height / 2;
- char *text = talloc_strdup(NULL, "{\\q2}");
+ char *text = talloc_asprintf(NULL, "{\\an6\\pos(%f,%f)}", sx, sy);
if (osd->progbar_type == 0 || osd->progbar_type >= 256) {
// no sym
@@ -207,30 +281,57 @@ static void update_progbar(struct osd_state *osd, struct osd_object *obj)
text = talloc_strdup_append_buffer(text, "{\\r}");
}
- //xxx space in normal font, because OSD font doesn't have a space
- text = talloc_strdup_append_buffer(text, "\\h");
- text = talloc_strdup_append_buffer(text, ASS_USE_OSD_FONT);
+ add_osd_ass_event(obj->osd_track, text);
+ talloc_free(text);
- text = mp_append_utf8_buffer(text, OSD_CODEPOINTS + OSD_PB_START);
- for (int n = 0; n < active; n++)
- text = mp_append_utf8_buffer(text, OSD_CODEPOINTS + OSD_PB_0);
- for (int n = 0; n < OSDBAR_ELEMS - active; n++)
- text = mp_append_utf8_buffer(text, OSD_CODEPOINTS + OSD_PB_1);
- text = mp_append_utf8_buffer(text, OSD_CODEPOINTS + OSD_PB_END);
+ struct ass_draw *d = &(struct ass_draw) { .scale = 4 };
+ // filled area
+ d->text = talloc_asprintf_append(d->text, "{\\pos(%f,%f)}", px, py);
+ ass_draw_start(d);
+ float pos = osd->progbar_value * width - border / 2;
+ ass_draw_rect_cw(d, 0, 0, pos, height);
+ ass_draw_stop(d);
+ add_osd_ass_event(obj->osd_track, d->text);
+ ass_draw_reset(d);
+
+ d->text = talloc_asprintf_append(d->text, "{\\pos(%f,%f)}", px, py);
+ ass_draw_start(d);
+
+ // the box
+ ass_draw_rect_cw(d, -border, -border, width + border, height + border);
+
+ // the "hole"
+ ass_draw_rect_ccw(d, 0, 0, width, height);
+
+ // chapter marks
+ for (int n = 0; n < osd->progbar_num_stops; n++) {
+ float s = osd->progbar_stops[n] * width;
+ float dent = border * 1.3;
+
+ if (s > dent && s < width - dent) {
+ ass_draw_move_to(d, s + dent, 0);
+ ass_draw_line_to(d, s, dent);
+ ass_draw_line_to(d, s - dent, 0);
+
+ ass_draw_move_to(d, s - dent, height);
+ ass_draw_line_to(d, s, height - dent);
+ ass_draw_line_to(d, s + dent, height);
+ }
+ }
- ASS_Event *event = get_osd_ass_event(obj->osd_track);
- event->Text = strdup(text);
- talloc_free(text);
+ ass_draw_stop(d);
+ add_osd_ass_event(obj->osd_track, d->text);
+ ass_draw_reset(d);
}
static void update_sub(struct osd_state *osd, struct osd_object *obj)
{
struct MPOpts *opts = osd->opts;
- if (!(vo_sub && opts->sub_visibility)) {
- clear_obj(obj);
+ clear_obj(obj);
+
+ if (!(vo_sub && opts->sub_visibility))
return;
- }
if (!obj->osd_track)
obj->osd_track = mp_ass_default_track(osd->osd_ass_library, osd->opts);
@@ -250,9 +351,8 @@ static void update_sub(struct osd_state *osd, struct osd_object *obj)
for (int n = 0; n < vo_sub->lines; n++)
text = talloc_asprintf_append_buffer(text, "%s\n", vo_sub->text[n]);
- ASS_Event *event = get_osd_ass_event(obj->osd_track);
char *escaped_text = mangle_ass(text);
- event->Text = strdup(escaped_text);
+ add_osd_ass_event(obj->osd_track, escaped_text);
talloc_free(escaped_text);
talloc_free(text);
}
diff --git a/sub/sub.h b/sub/sub.h
index 2055799d72..660fd9ccc9 100644
--- a/sub/sub.h
+++ b/sub/sub.h
@@ -129,8 +129,13 @@ struct osd_state {
bool want_redraw;
- char *osd_text; // OSDTYPE_OSD
- int progbar_type, progbar_value; // OSDTYPE_PROGBAR
+ // OSDTYPE_OSD
+ char *osd_text;
+ // OSDTYPE_PROGBAR
+ int progbar_type; // <0: disabled, 1-255: symbol, else: no symbol
+ float progbar_value; // range 0.0-1.0
+ float *progbar_stops; // used for chapter indicators (0.0-1.0 each)
+ int progbar_num_stops;
int switch_sub_id;