From e44911142914783c9ec717f329bd9b6a8bb9b70e Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 17 Dec 2013 00:53:22 +0100 Subject: Move mpvcore/player/ to player/ --- player/osd.c | 518 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 518 insertions(+) create mode 100644 player/osd.c (limited to 'player/osd.c') diff --git a/player/osd.c b/player/osd.c new file mode 100644 index 0000000000..42409b555f --- /dev/null +++ b/player/osd.c @@ -0,0 +1,518 @@ +/* + * This file is part of MPlayer. + * + * MPlayer 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. + * + * MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "talloc.h" + +#include "mpvcore/mp_msg.h" +#include "mpvcore/options.h" +#include "mpvcore/mp_common.h" +#include "mpvcore/m_property.h" +#include "mpvcore/encode.h" + +#include "osdep/getch2.h" +#include "osdep/timer.h" + +#include "demux/demux.h" +#include "sub/osd.h" + +#include "mp_core.h" +#include "command.h" + +#define saddf(var, ...) (*(var) = talloc_asprintf_append((*var), __VA_ARGS__)) + +// append time in the hh:mm:ss format (plus fractions if wanted) +static void sadd_hhmmssff(char **buf, double time, bool fractions) +{ + char *s = mp_format_time(time, fractions); + *buf = talloc_strdup_append(*buf, s); + talloc_free(s); +} + +static void sadd_percentage(char **buf, int percent) { + if (percent >= 0) + *buf = talloc_asprintf_append(*buf, " (%d%%)", percent); +} + +static int get_term_width(void) +{ + get_screen_size(); + int width = screen_width > 0 ? screen_width : 80; +#if defined(__MINGW32__) || defined(__CYGWIN__) + /* Windows command line is broken (MinGW's rxvt works, but we + * should not depend on that). */ + width--; +#endif + return width; +} + +void write_status_line(struct MPContext *mpctx, const char *line) +{ + struct MPOpts *opts = mpctx->opts; + if (opts->slave_mode) { + mp_msg(MSGT_STATUSLINE, MSGL_STATUS, "%s\n", line); + } else if (erase_to_end_of_line) { + mp_msg(MSGT_STATUSLINE, MSGL_STATUS, + "%s%s\r", line, erase_to_end_of_line); + } else { + int pos = strlen(line); + int width = get_term_width() - pos; + mp_msg(MSGT_STATUSLINE, MSGL_STATUS, "%s%*s\r", line, width, ""); + } +} + +void print_status(struct MPContext *mpctx) +{ + struct MPOpts *opts = mpctx->opts; + + update_window_title(mpctx, false); + + if (opts->quiet) + return; + + if (opts->status_msg) { + char *r = mp_property_expand_string(mpctx, opts->status_msg); + write_status_line(mpctx, r); + talloc_free(r); + return; + } + + char *line = NULL; + + // Playback status + if (mpctx->paused_for_cache && !opts->pause) { + saddf(&line, "(Buffering) "); + } else if (mpctx->paused) { + saddf(&line, "(Paused) "); + } + + if (mpctx->d_audio) + saddf(&line, "A"); + if (mpctx->d_video) + saddf(&line, "V"); + saddf(&line, ": "); + + // Playback position + double cur = get_current_time(mpctx); + sadd_hhmmssff(&line, cur, mpctx->opts->osd_fractions); + + double len = get_time_length(mpctx); + if (len >= 0) { + saddf(&line, " / "); + sadd_hhmmssff(&line, len, mpctx->opts->osd_fractions); + } + + sadd_percentage(&line, get_percent_pos(mpctx)); + + // other + if (opts->playback_speed != 1) + saddf(&line, " x%4.2f", opts->playback_speed); + + // A-V sync + if (mpctx->d_audio && mpctx->d_video && mpctx->sync_audio_to_video) { + if (mpctx->last_av_difference != MP_NOPTS_VALUE) + saddf(&line, " A-V:%7.3f", mpctx->last_av_difference); + else + saddf(&line, " A-V: ???"); + if (fabs(mpctx->total_avsync_change) > 0.05) + saddf(&line, " ct:%7.3f", mpctx->total_avsync_change); + } + +#if HAVE_ENCODING + double position = get_current_pos_ratio(mpctx, true); + char lavcbuf[80]; + if (encode_lavc_getstatus(mpctx->encode_lavc_ctx, lavcbuf, sizeof(lavcbuf), + position) >= 0) + { + // encoding stats + saddf(&line, " %s", lavcbuf); + } else +#endif + { + // VO stats + if (mpctx->d_video && mpctx->drop_frame_cnt) + saddf(&line, " Late: %d", mpctx->drop_frame_cnt); + } + + int cache = mp_get_cache_percent(mpctx); + if (cache >= 0) + saddf(&line, " Cache: %d%%", cache); + + // end + write_status_line(mpctx, line); + talloc_free(line); +} + +typedef struct mp_osd_msg mp_osd_msg_t; +struct mp_osd_msg { + /// Previous message on the stack. + mp_osd_msg_t *prev; + /// Message text. + char *msg; + int id, level, started; + /// Display duration in seconds. + double time; + // Show full OSD for duration of message instead of msg + // (osd_show_progression command) + bool show_position; +}; + +// time is in ms +static mp_osd_msg_t *add_osd_msg(struct MPContext *mpctx, int id, int level, + int time) +{ + rm_osd_msg(mpctx, id); + mp_osd_msg_t *msg = talloc_struct(mpctx, mp_osd_msg_t, { + .prev = mpctx->osd_msg_stack, + .msg = "", + .id = id, + .level = level, + .time = time / 1000.0, + }); + mpctx->osd_msg_stack = msg; + return msg; +} + +static void set_osd_msg_va(struct MPContext *mpctx, int id, int level, int time, + const char *fmt, va_list ap) +{ + if (level == OSD_LEVEL_INVISIBLE) + return; + mp_osd_msg_t *msg = add_osd_msg(mpctx, id, level, time); + msg->msg = talloc_vasprintf(msg, fmt, ap); +} + +void set_osd_msg(struct MPContext *mpctx, int id, int level, int time, + const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + set_osd_msg_va(mpctx, id, level, time, fmt, ap); + va_end(ap); +} + +/** + * \brief Remove a message from the OSD stack + * + * This function can be used to get rid of a message right away. + * + */ + +void rm_osd_msg(struct MPContext *mpctx, int id) +{ + mp_osd_msg_t *msg, *last = NULL; + + // Search for the msg + for (msg = mpctx->osd_msg_stack; msg && msg->id != id; + last = msg, msg = msg->prev) ; + if (!msg) + return; + + // Detach it from the stack and free it + if (last) + last->prev = msg->prev; + else + mpctx->osd_msg_stack = msg->prev; + talloc_free(msg); +} + +/** + * \brief Get the current message from the OSD stack. + * + * This function decrements the message timer and destroys the old ones. + * The message that should be displayed is returned (if any). + * + */ + +static mp_osd_msg_t *get_osd_msg(struct MPContext *mpctx) +{ + struct MPOpts *opts = mpctx->opts; + mp_osd_msg_t *msg, *prev, *last = NULL; + double now = mp_time_sec(); + double diff; + char hidden_dec_done = 0; + + if (mpctx->osd_visible && now >= mpctx->osd_visible) { + mpctx->osd_visible = 0; + mpctx->osd->progbar_type = -1; // disable + osd_changed(mpctx->osd, OSDTYPE_PROGBAR); + } + if (mpctx->osd_function_visible && now >= mpctx->osd_function_visible) { + mpctx->osd_function_visible = 0; + mpctx->osd_function = 0; + } + + if (!mpctx->osd_last_update) + mpctx->osd_last_update = now; + diff = now >= mpctx->osd_last_update ? now - mpctx->osd_last_update : 0; + + mpctx->osd_last_update = now; + + // Look for the first message in the stack with high enough level. + for (msg = mpctx->osd_msg_stack; msg; last = msg, msg = prev) { + prev = msg->prev; + if (msg->level > opts->osd_level && hidden_dec_done) + continue; + // The message has a high enough level or it is the first hidden one + // in both cases we decrement the timer or kill it. + if (!msg->started || msg->time > diff) { + if (msg->started) + msg->time -= diff; + else + msg->started = 1; + // display it + if (msg->level <= opts->osd_level) + return msg; + hidden_dec_done = 1; + continue; + } + // kill the message + talloc_free(msg); + if (last) { + last->prev = prev; + msg = last; + } else { + mpctx->osd_msg_stack = prev; + msg = NULL; + } + } + // Nothing found + return NULL; +} + +// type: mp_osd_font_codepoints, ASCII, or OSD_BAR_* +// name: fallback for terminal OSD +void set_osd_bar(struct MPContext *mpctx, int type, const char *name, + double min, double max, double val) +{ + struct MPOpts *opts = mpctx->opts; + if (opts->osd_level < 1 || !opts->osd_bar_visible) + return; + + if (mpctx->video_out && opts->term_osd != 1) { + mpctx->osd_visible = mp_time_sec() + opts->osd_duration / 1000.0; + mpctx->osd->progbar_type = type; + mpctx->osd->progbar_value = (val - min) / (max - min); + mpctx->osd->progbar_num_stops = 0; + osd_changed(mpctx->osd, OSDTYPE_PROGBAR); + return; + } + + set_osd_msg(mpctx, OSD_MSG_BAR, 1, opts->osd_duration, "%s: %d %%", + name, ROUND(100 * (val - min) / (max - min))); +} + +// Update a currently displayed bar of the same type, without resetting the +// timer. +static void update_osd_bar(struct MPContext *mpctx, int type, + double min, double max, double val) +{ + if (mpctx->osd->progbar_type == type) { + float new_value = (val - min) / (max - min); + if (new_value != mpctx->osd->progbar_value) { + mpctx->osd->progbar_value = new_value; + osd_changed(mpctx->osd, OSDTYPE_PROGBAR); + } + } +} + +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); + } + } + } + } +} + +// osd_function is the symbol appearing in the video status, such as OSD_PLAY +void set_osd_function(struct MPContext *mpctx, int osd_function) +{ + struct MPOpts *opts = mpctx->opts; + + mpctx->osd_function = osd_function; + mpctx->osd_function_visible = mp_time_sec() + opts->osd_duration / 1000.0; +} + +/** + * \brief Display text subtitles on the OSD + */ +void set_osd_subtitle(struct MPContext *mpctx, const char *text) +{ + if (!text) + text = ""; + if (strcmp(mpctx->osd->sub_text, text) != 0) { + osd_set_sub(mpctx->osd, text); + if (!mpctx->video_out) { + rm_osd_msg(mpctx, OSD_MSG_SUB_BASE); + if (text && text[0]) + set_osd_msg(mpctx, OSD_MSG_SUB_BASE, 1, INT_MAX, "%s", text); + } + } + if (!text[0]) + rm_osd_msg(mpctx, OSD_MSG_SUB_BASE); +} + +// sym == mpctx->osd_function +static void saddf_osd_function_sym(char **buffer, int sym) +{ + char temp[10]; + osd_get_function_sym(temp, sizeof(temp), sym); + saddf(buffer, "%s ", temp); +} + +static void sadd_osd_status(char **buffer, struct MPContext *mpctx, bool full) +{ + bool fractions = mpctx->opts->osd_fractions; + int sym = mpctx->osd_function; + if (!sym) { + if (mpctx->paused_for_cache && !mpctx->opts->pause) { + sym = OSD_CLOCK; + } else if (mpctx->paused || mpctx->step_frames) { + sym = OSD_PAUSE; + } else { + sym = OSD_PLAY; + } + } + saddf_osd_function_sym(buffer, sym); + char *custom_msg = mpctx->opts->osd_status_msg; + if (custom_msg && full) { + char *text = mp_property_expand_string(mpctx, custom_msg); + *buffer = talloc_strdup_append(*buffer, text); + talloc_free(text); + } else { + sadd_hhmmssff(buffer, get_current_time(mpctx), fractions); + if (full) { + saddf(buffer, " / "); + sadd_hhmmssff(buffer, get_time_length(mpctx), fractions); + sadd_percentage(buffer, get_percent_pos(mpctx)); + int cache = mp_get_cache_percent(mpctx); + if (cache >= 0) + saddf(buffer, " Cache: %d%%", cache); + } + } +} + +// OSD messages initated by seeking commands are added lazily with this +// function, because multiple successive seek commands can be coalesced. +static void add_seek_osd_messages(struct MPContext *mpctx) +{ + if (mpctx->add_osd_seek_info & OSD_SEEK_INFO_BAR) { + double pos = get_current_pos_ratio(mpctx, false); + set_osd_bar(mpctx, OSD_BAR_SEEK, "Position", 0, 1, MPCLAMP(pos, 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, + mpctx->opts->osd_duration); + msg->show_position = true; + } + if (mpctx->add_osd_seek_info & OSD_SEEK_INFO_CHAPTER_TEXT) { + char *chapter = chapter_display_name(mpctx, get_current_chapter(mpctx)); + set_osd_msg(mpctx, OSD_MSG_TEXT, 1, mpctx->opts->osd_duration, + "Chapter: %s", chapter); + talloc_free(chapter); + } + if ((mpctx->add_osd_seek_info & OSD_SEEK_INFO_EDITION) + && mpctx->master_demuxer) + { + set_osd_msg(mpctx, OSD_MSG_TEXT, 1, mpctx->opts->osd_duration, + "Playing edition %d of %d.", + mpctx->master_demuxer->edition + 1, + mpctx->master_demuxer->num_editions); + } + mpctx->add_osd_seek_info = 0; +} + +/** + * \brief Update the OSD message line. + * + * This function displays the current message on the vo OSD or on the term. + * If the stack is empty and the OSD level is high enough the timer + * is displayed (only on the vo OSD). + * + */ + +void update_osd_msg(struct MPContext *mpctx) +{ + struct MPOpts *opts = mpctx->opts; + struct osd_state *osd = mpctx->osd; + + add_seek_osd_messages(mpctx); + double pos = get_current_pos_ratio(mpctx, false); + update_osd_bar(mpctx, OSD_BAR_SEEK, 0, 1, MPCLAMP(pos, 0, 1)); + + // Look if we have a msg + mp_osd_msg_t *msg = get_osd_msg(mpctx); + if (msg && !msg->show_position) { + if (mpctx->video_out && opts->term_osd != 1) { + osd_set_text(osd, msg->msg); + } else if (opts->term_osd) { + if (strcmp(mpctx->terminal_osd_text, msg->msg)) { + talloc_free(mpctx->terminal_osd_text); + mpctx->terminal_osd_text = talloc_strdup(mpctx, msg->msg); + // Multi-line message => clear what will be the second line + write_status_line(mpctx, ""); + mp_msg(MSGT_CPLAYER, MSGL_STATUS, "%s%s\n", opts->term_osd_esc, + mpctx->terminal_osd_text); + print_status(mpctx); + } + } + return; + } + + int osd_level = opts->osd_level; + if (msg && msg->show_position) + osd_level = 3; + + if (mpctx->video_out && opts->term_osd != 1) { + // fallback on the timer + char *text = NULL; + + if (osd_level >= 2) + sadd_osd_status(&text, mpctx, osd_level == 3); + + osd_set_text(osd, text); + talloc_free(text); + return; + } + + // Clear the term osd line + if (opts->term_osd && mpctx->terminal_osd_text[0]) { + mpctx->terminal_osd_text[0] = '\0'; + mp_msg(MSGT_CPLAYER, MSGL_STATUS, "%s\n", opts->term_osd_esc); + } +} -- cgit v1.2.3