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); +} - 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; +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); - // 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; + clear_obj(obj); - int active = (osd->progbar_value * OSDBAR_ELEMS + 255) / 256; - active = FFMIN(OSDBAR_ELEMS, FFMAX(active, 0)); + if (osd->progbar_type < 0) + return; - char *text = talloc_strdup(NULL, "{\\q2}"); + float sx = px - border * 2 - height / 4; // includes additional spacing + float sy = py + height / 2; + + 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); - - 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); - - ASS_Event *event = get_osd_ass_event(obj->osd_track); - event->Text = strdup(text); + add_osd_ass_event(obj->osd_track, text); talloc_free(text); + + 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_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;