diff --git a/Makefile b/Makefile index 1662f6f1e1..f7b94f44e9 100644 --- a/Makefile +++ b/Makefile @@ -227,7 +227,6 @@ SOURCES = talloc.c \ stream/url.c \ sub/dec_sub.c \ sub/draw_bmp.c \ - sub/find_sub.c \ sub/find_subfiles.c \ sub/img_convert.c \ sub/sd_lavc.c \ diff --git a/core/command.c b/core/command.c index 3ee8f39c3d..7eb36be5f3 100644 --- a/core/command.c +++ b/core/command.c @@ -1307,7 +1307,6 @@ static int mp_property_sub_visibility(m_option_t *prop, int action, switch (action) { case M_PROPERTY_SET: opts->sub_visibility = *(int *)arg; - vo_osd_changed(OSDTYPE_SUBTITLE); if (vo_spudec) vo_osd_changed(OSDTYPE_SPU); return M_PROPERTY_OK; @@ -1991,26 +1990,18 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) } case MP_CMD_SUB_STEP: +#ifdef CONFIG_ASS if (sh_video) { int movement = cmd->args[0].v.i; - struct track *track = mpctx->current_track[STREAM_SUB]; - bool available = false; - if (track && track->subdata) { - available = true; - step_sub(track->subdata, mpctx->video_pts, movement); - } -#ifdef CONFIG_ASS struct ass_track *ass_track = sub_get_ass_track(mpctx->osd); if (ass_track) { - available = true; + set_osd_tmsg(mpctx, OSD_MSG_SUB_DELAY, osdl, osd_duration, + "Sub delay: %d ms", ROUND(sub_delay * 1000)); sub_delay += ass_step_sub(ass_track, (mpctx->video_pts + sub_delay) * 1000 + .5, movement) / 1000.; } -#endif - if (available) - set_osd_tmsg(mpctx, OSD_MSG_SUB_DELAY, osdl, osd_duration, - "Sub delay: %d ms", ROUND(sub_delay * 1000)); } +#endif break; case MP_CMD_OSD: { @@ -2194,7 +2185,6 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) if (tv_channel_list) { set_osd_tmsg(mpctx, OSD_MSG_TV_CHANNEL, osdl, osd_duration, "Channel: %s", tv_channel_current->name); - //vo_osd_changed(OSDTYPE_SUBTITLE); } } #ifdef CONFIG_PVR @@ -2232,7 +2222,6 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) if (tv_channel_list) { set_osd_tmsg(mpctx, OSD_MSG_TV_CHANNEL, osdl, osd_duration, "Channel: %s", tv_channel_current->name); - //vo_osd_changed(OSDTYPE_SUBTITLE); } } #ifdef CONFIG_PVR @@ -2265,7 +2254,6 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) if (tv_channel_list) { set_osd_tmsg(mpctx, OSD_MSG_TV_CHANNEL, osdl, osd_duration, "Channel: %s", tv_channel_current->name); - //vo_osd_changed(OSDTYPE_SUBTITLE); } } #ifdef CONFIG_PVR diff --git a/core/mp_core.h b/core/mp_core.h index 9e61c8ffa3..665588c5c0 100644 --- a/core/mp_core.h +++ b/core/mp_core.h @@ -22,8 +22,6 @@ #include #include "core/options.h" -#include "sub/subreader.h" -#include "sub/find_subfiles.h" #include "audio/mixer.h" #include "demux/demux.h" @@ -112,9 +110,6 @@ struct track { // External text subtitle using libass subtitle renderer. // The sh_sub is a dummy and doesn't belong to a demuxer. struct sh_sub *sh_sub; - - // External text subtitle using non-libass subtitle renderer. - struct sub_data *subdata; }; enum { @@ -129,7 +124,6 @@ typedef struct MPContext { struct osd_state *osd; struct mp_osd_msg *osd_msg_stack; char *terminal_osd_text; - subtitle subs; // subtitle list used when reading subtitles from demuxer int add_osd_seek_info; // bitfield of enum mp_osd_seek_info double osd_visible; // for the osd bar only diff --git a/core/mplayer.c b/core/mplayer.c index 52aa34058c..602d1f0616 100644 --- a/core/mplayer.c +++ b/core/mplayer.c @@ -74,6 +74,7 @@ #include "sub/subreader.h" #include "sub/find_subfiles.h" #include "sub/dec_sub.h" +#include "sub/sd.h" #include "core/mp_osd.h" #include "video/out/vo.h" @@ -284,8 +285,6 @@ static void print_stream(struct MPContext *mpctx, struct track *t) const char *codec = s ? s->codec : NULL; if (!codec && t->sh_sub) // external subs hack codec = t->sh_sub->gsh->codec; - if (!codec && t->subdata) - codec = t->subdata->codec; mp_msg(MSGT_CPLAYER, MSGL_INFO, " (%s)", codec ? codec : ""); if (t->is_external) mp_msg(MSGT_CPLAYER, MSGL_INFO, " (external)"); @@ -1052,38 +1051,36 @@ struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename, float fps, int noerr) { struct MPOpts *opts = &mpctx->opts; - sub_data *subd = NULL; struct sh_sub *sh = NULL; + struct ass_track *asst = NULL; + const char *codec = NULL; if (filename == NULL) return NULL; - if (opts->ass_enabled) { + // Note: no text subtitles without libass. This is mainly because sd_ass is + // used for rendering. Even when showing subtitles with term-osd, going + // through sd_ass makes the code much simpler, as sd_ass can handle all + // the weird special-cases. #ifdef CONFIG_ASS - struct ass_track *asst = mp_ass_read_stream(mpctx->ass_library, - filename, sub_cp); - bool is_native_ass = asst; - const char *codec = NULL; - if (!asst) { - subd = sub_read_file(filename, fps, &mpctx->opts); - if (subd) { - codec = subd->codec; - asst = mp_ass_read_subdata(mpctx->ass_library, opts, subd, fps); - talloc_free(subd); - subd = NULL; - } - } - if (asst) { - sh = sd_ass_create_from_track(asst, is_native_ass, opts); - if (codec) - sh->gsh->codec = codec; + if (opts->ass_enabled) { + asst = mp_ass_read_stream(mpctx->ass_library, filename, sub_cp); + codec = "ass"; + } + if (!asst) { + sub_data *subd = sub_read_file(filename, fps, &mpctx->opts); + if (subd) { + codec = subd->codec; + asst = mp_ass_read_subdata(mpctx->ass_library, opts, subd, fps); } + talloc_free(subd); + } + if (asst) + sh = sd_ass_create_from_track(asst, codec, opts); #endif - } else - subd = sub_read_file(filename, fps, &mpctx->opts); - - if (!sh && !subd) { + if (!sh) { + // Used with image subtitles. struct track *ext = open_external_file(mpctx, filename, NULL, 0, STREAM_SUB); if (ext) @@ -1101,7 +1098,6 @@ struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename, .demuxer_id = -1, .is_external = true, .sh_sub = talloc_steal(track, sh), - .subdata = talloc_steal(track, subd), .external_filename = talloc_strdup(track, filename), }; MP_TARRAY_APPEND(mpctx, mpctx->tracks, mpctx->num_tracks, track); @@ -1545,23 +1541,16 @@ void set_osd_function(struct MPContext *mpctx, int osd_function) /** * \brief Display text subtitles on the OSD */ -void set_osd_subtitle(struct MPContext *mpctx, subtitle *subs) +static void set_osd_subtitle(struct MPContext *mpctx, const char *text) { - int i; - vo_sub = subs; - vo_osd_changed(OSDTYPE_SUBTITLE); - if (!mpctx->sh_video) { - // reverse order, since newest set_osd_msg is displayed first - for (i = SUB_MAX_TEXT - 1; i >= 0; i--) { - if (!subs || i >= subs->lines || !subs->text[i]) - rm_osd_msg(mpctx, OSD_MSG_SUB_BASE + i); - else { - // HACK: currently display time for each sub line - // except the last is set to 2 seconds. - int display_time = i == subs->lines - 1 ? 180000 : 2000; - set_osd_msg(mpctx, OSD_MSG_SUB_BASE + i, 1, display_time, - "%s", subs->text[i]); - } + if (!text) + text = ""; + if (strcmp(mpctx->osd->sub_text, text) != 0) { + osd_set_sub(mpctx->osd, text); + if (!mpctx->sh_video) { + 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); } } } @@ -1877,9 +1866,7 @@ static void reset_subtitles(struct MPContext *mpctx) { if (mpctx->sh_sub) sub_reset(mpctx->sh_sub, mpctx->osd); - sub_clear_text(&mpctx->subs, MP_NOPTS_VALUE); - if (vo_sub) - set_osd_subtitle(mpctx, NULL); + set_osd_subtitle(mpctx, NULL); if (vo_spudec) { spudec_reset(vo_spudec); vo_osd_changed(OSDTYPE_SPU); @@ -1888,33 +1875,22 @@ static void reset_subtitles(struct MPContext *mpctx) static void update_subtitles(struct MPContext *mpctx, double refpts_tl) { - struct MPOpts *opts = &mpctx->opts; - struct sh_video *sh_video = mpctx->sh_video; struct sh_sub *sh_sub = mpctx->sh_sub; struct demux_stream *d_sub = sh_sub ? sh_sub->ds : NULL; unsigned char *packet = NULL; int len; const char *type = sh_sub ? sh_sub->gsh->codec : NULL; - mpctx->osd->sub_offset = mpctx->video_offset; - struct track *track = mpctx->current_track[STREAM_SUB]; if (!track) return; - if (!track->under_timeline) - mpctx->osd->sub_offset = 0; + double video_offset = track->under_timeline ? mpctx->video_offset : 0; - double refpts_s = refpts_tl - mpctx->osd->sub_offset; - double curpts_s = refpts_s + sub_delay; + mpctx->osd->sub_offset = video_offset - sub_delay; - // find sub - if (track->subdata) { - if (sub_fps == 0) - sub_fps = sh_video ? sh_video->fps : 25; - find_sub(mpctx, track->subdata, curpts_s * - (track->subdata->sub_uses_time ? 100. : sub_fps)); - } + 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)) { @@ -1955,7 +1931,7 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl) if (timestamp >= 0) spudec_assemble(vo_spudec, packet, len, timestamp); } - } else if (d_sub && (is_text_sub(type) || (sh_sub && sh_sub->active))) { + } else 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); @@ -1967,7 +1943,7 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl) "Sub early: c_pts=%5.3f s_pts=%5.3f\n", curpts_s, subpts_s); // Libass handled subs can be fed to it in advance - if (!opts->ass_enabled || !is_text_sub(type)) + if (!sub_accept_packets_in_advance(sh_sub)) break; // Try to avoid demuxing whole file at once if (non_interleaved && subpts_s > curpts_s + 1) @@ -1984,41 +1960,19 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl) len = FFMIN(len - 2, AV_RB16(packet)); packet += 2; } - if (sh_sub && sh_sub->active) { - sub_decode(sh_sub, mpctx->osd, packet, len, subpts_s, duration); - } else if (subpts_s != MP_NOPTS_VALUE) { - // text sub - if (duration < 0) - sub_clear_text(&mpctx->subs, MP_NOPTS_VALUE); - if (is_ass_sub(type)) { // ssa/ass subs without libass => convert to plaintext - int i; - unsigned char *p = packet; - for (i = 0; i < 8 && *p != '\0'; p++) - if (*p == ',') - i++; - if (*p == '\0') /* Broken line? */ - continue; - len -= p - packet; - packet = p; - } - double endpts_s = MP_NOPTS_VALUE; - if (subpts_s != MP_NOPTS_VALUE && duration >= 0) - endpts_s = subpts_s + duration; - sub_add_text(&mpctx->subs, packet, len, endpts_s); - set_osd_subtitle(mpctx, &mpctx->subs); - } + sub_decode(sh_sub, mpctx->osd, packet, len, subpts_s, duration); if (non_interleaved) ds_get_next_pts(d_sub); } - if (!opts->ass_enabled) - if (sub_clear_text(&mpctx->subs, curpts_s)) - set_osd_subtitle(mpctx, &mpctx->subs); } 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)); } static int check_framedrop(struct MPContext *mpctx, double frame_time) @@ -2130,12 +2084,8 @@ static void reinit_subs(struct MPContext *mpctx) mpctx->initialized_flags |= INITIALIZED_SUB; - if (track->subdata || track->sh_sub) { -#ifdef CONFIG_ASS - if (opts->ass_enabled && track->sh_sub) - sub_init(track->sh_sub, mpctx->osd); -#endif - vo_osd_changed(OSDTYPE_SUBTITLE); + if (track->sh_sub) { + sub_init(track->sh_sub, mpctx->osd); } else if (track->stream) { struct stream *s = track->demuxer ? track->demuxer->stream : NULL; if (s && s->type == STREAMTYPE_DVD) @@ -2153,6 +2103,12 @@ static void reinit_subs(struct MPContext *mpctx) else sub_init(mpctx->sh_sub, mpctx->osd); } + + // Decides whether to use OSD path or normal subtitle rendering path. + mpctx->osd->render_bitmap_subs = true; + struct sh_sub *sh_sub = mpctx->osd->sh_sub; + if (sh_sub && sh_sub->active && sh_sub->sd_driver->get_text) + mpctx->osd->render_bitmap_subs = opts->ass_enabled; } static char *track_layout_hash(struct MPContext *mpctx) @@ -4551,7 +4507,6 @@ terminate_playback: // don't jump here after ao/vo/getch initialization! talloc_free(mpctx->resolve_result); mpctx->resolve_result = NULL; - vo_sub = NULL; #ifdef CONFIG_ASS if (mpctx->osd->ass_renderer) ass_renderer_done(mpctx->osd->ass_renderer); diff --git a/core/mplayer.h b/core/mplayer.h index b96f814b68..825458b6f5 100644 --- a/core/mplayer.h +++ b/core/mplayer.h @@ -25,9 +25,6 @@ struct MPContext; struct MPOpts; -struct subtitle; - -void set_osd_subtitle(struct MPContext *mpctx, struct subtitle *subs); struct mp_resolve_result { char *url; diff --git a/sub/dec_sub.c b/sub/dec_sub.c index d3cedea80d..3de7d1223d 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -51,13 +51,13 @@ bool is_dvd_sub(const char *t) void sub_init(struct sh_sub *sh, struct osd_state *osd) { - struct MPOpts *opts = sh->opts; + const char *format = sh->gsh->codec; assert(!osd->sh_sub); - if (sd_lavc.probe(sh)) + if (sd_lavc.supports_format(format)) sh->sd_driver = &sd_lavc; #ifdef CONFIG_ASS - if (opts->ass_enabled && sd_ass.probe(sh)) + if (sd_ass.supports_format(format)) sh->sd_driver = &sd_ass; #endif if (sh->sd_driver) { @@ -70,6 +70,11 @@ void sub_init(struct sh_sub *sh, struct osd_state *osd) } } +bool sub_accept_packets_in_advance(struct sh_sub *sh) +{ + return sh->active && sh->sd_driver->accept_packets_in_advance; +} + void sub_decode(struct sh_sub *sh, struct osd_state *osd, void *data, int data_len, double pts, double duration) { @@ -101,6 +106,19 @@ void sub_get_bitmaps(struct osd_state *osd, struct mp_osd_res dim, double pts, osd->switch_sub_id = 0; } +char *sub_get_text(struct osd_state *osd, double pts) +{ + struct MPOpts *opts = osd->opts; + char *text = NULL; + if (!opts->sub_visibility || !osd->sh_sub || !osd->sh_sub->active) { + // - + } else { + if (osd->sh_sub->sd_driver->get_text) + text = osd->sh_sub->sd_driver->get_text(osd->sh_sub, osd, pts); + } + return text; +} + void sub_reset(struct sh_sub *sh, struct osd_state *osd) { if (sh->active && sh->sd_driver->reset) diff --git a/sub/dec_sub.h b/sub/dec_sub.h index 593eac1e03..52fa05eebc 100644 --- a/sub/dec_sub.h +++ b/sub/dec_sub.h @@ -14,18 +14,19 @@ bool is_text_sub(const char *t); bool is_ass_sub(const char *t); bool is_dvd_sub(const char *t); +bool sub_accept_packets_in_advance(struct sh_sub *sh); void sub_decode(struct sh_sub *sh, struct osd_state *osd, void *data, int data_len, double pts, double duration); void sub_get_bitmaps(struct osd_state *osd, struct mp_osd_res dim, double pts, struct sub_bitmaps *res); +char *sub_get_text(struct osd_state *osd, double pts); void sub_init(struct sh_sub *sh, struct osd_state *osd); void sub_reset(struct sh_sub *sh, struct osd_state *osd); void sub_switchoff(struct sh_sub *sh, struct osd_state *osd); void sub_uninit(struct sh_sub *sh); struct sh_sub *sd_ass_create_from_track(struct ass_track *track, - bool vsfilter_aspect, - struct MPOpts *opts); + const char *codec, struct MPOpts *opts); #ifdef CONFIG_ASS struct ass_track *sub_get_ass_track(struct osd_state *osd); diff --git a/sub/find_sub.c b/sub/find_sub.c deleted file mode 100644 index 5feef2a3e9..0000000000 --- a/sub/find_sub.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * .SUB - * - * 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 "config.h" - -#include - -#include "sub.h" -#include "subreader.h" - -#include "core/mp_msg.h" -#include "core/mp_common.h" -#include "core/mplayer.h" - -static int current_sub=0; - -//static subtitle* subtitles=NULL; -static int nosub_range_start=-1; -static int nosub_range_end=-1; -static const sub_data *last_sub_data = NULL; - -void step_sub(sub_data *subd, float pts, int movement) { - subtitle *subs; - int key; - - if (subd == NULL) return; - subs = subd->subtitles; - key = (pts+sub_delay) * (subd->sub_uses_time ? 100 : sub_fps); - - /* Tell the OSD subsystem that the OSD contents will change soon */ - vo_osd_changed(OSDTYPE_SUBTITLE); - - /* If we are moving forward, don't count the next (current) subtitle - * if we haven't displayed it yet. Same when moving other direction. - */ - if (movement > 0 && key < subs[current_sub].start) - movement--; - if (movement < 0 && key >= subs[current_sub].end) - movement++; - - /* Never move beyond first or last subtitle. */ - if (current_sub+movement < 0) - movement = 0-current_sub; - if (current_sub+movement >= subd->sub_num) - movement = subd->sub_num - current_sub - 1; - - current_sub += movement; - sub_delay = subs[current_sub].start / (subd->sub_uses_time ? 100 : sub_fps) - pts; -} - -void find_sub(struct MPContext *mpctx, sub_data* subd,int key){ - subtitle *subs; - subtitle *new_sub = NULL; - int i,j; - - if ( !subd || subd->sub_num == 0) return; - subs = subd->subtitles; - - if (last_sub_data != subd) { - // Sub data changed, reset nosub range. - last_sub_data = subd; - nosub_range_start = -1; - nosub_range_end = -1; - } - - if(vo_sub){ - if(key>=vo_sub->start && key<=vo_sub->end) return; // OK! - } else { - if(key>nosub_range_start && key=0 && current_sub+1 < subd->sub_num){ - if(key>subs[current_sub].end && key=new_sub->start && key<=new_sub->end) goto update; // OK! - } - -// printf("\r---- sub log search... ----\n"); - - // use logarithmic search: - i=0; - j = subd->sub_num - 1; -// printf("Searching %d in %d..%d\n",key,subs[i].start,subs[j].end); - while(j>=i){ - current_sub=(i+j+1)/2; - new_sub=&subs[current_sub]; - if(keystart) j=current_sub-1; - else if(key>new_sub->end) i=current_sub+1; - else goto update; // found! - } -// if(key>=new_sub->start && key<=new_sub->end) return; // OK! - - // check where are we... - if(keystart){ - if(current_sub<=0){ - // before the first sub - nosub_range_start=key-1; // tricky - nosub_range_end=new_sub->start; -// printf("FIRST... key=%d end=%d \n",key,new_sub->start); - new_sub=NULL; - goto update; - } - --current_sub; - if(key>subs[current_sub].end && keyend) printf("JAJJ! "); else - if(current_sub+1 >= subd->sub_num){ - // at the end? - nosub_range_start=new_sub->end; - nosub_range_end=0x7FFFFFFF; // MAXINT -// printf("END!?\n"); - new_sub=NULL; - goto update; - } else - if(key>subs[current_sub].end && keystart,(int)new_sub->end,current_sub); - - new_sub=NULL; // no sub here -update: - set_osd_subtitle(mpctx, new_sub); -} diff --git a/sub/osd_libass.c b/sub/osd_libass.c index d157f7925c..d8496c2f3b 100644 --- a/sub/osd_libass.c +++ b/sub/osd_libass.c @@ -354,7 +354,7 @@ static void update_sub(struct osd_state *osd, struct osd_object *obj) clear_obj(obj); - if (!(vo_sub && opts->sub_visibility)) + if (!osd->sub_text || !osd->sub_text[0]) return; if (!obj->osd_track) @@ -370,15 +370,9 @@ static void update_sub(struct osd_state *osd, struct osd_object *obj) ass_set_line_position(osd->osd_render, 100 - sub_pos); #endif - char *text = talloc_strdup(NULL, ""); - - for (int n = 0; n < vo_sub->lines; n++) - text = talloc_asprintf_append_buffer(text, "%s\n", vo_sub->text[n]); - - char *escaped_text = mangle_ass(text); + char *escaped_text = mangle_ass(osd->sub_text); add_osd_ass_event(obj->osd_track, escaped_text); talloc_free(escaped_text); - talloc_free(text); } static void update_object(struct osd_state *osd, struct osd_object *obj) @@ -387,7 +381,7 @@ static void update_object(struct osd_state *osd, struct osd_object *obj) case OSDTYPE_OSD: update_osd(osd, obj); break; - case OSDTYPE_SUBTITLE: + case OSDTYPE_SUBTEXT: update_sub(osd, obj); break; case OSDTYPE_PROGBAR: diff --git a/sub/sd.h b/sub/sd.h index 881c429689..123a9bc45d 100644 --- a/sub/sd.h +++ b/sub/sd.h @@ -4,13 +4,15 @@ #include "dec_sub.h" struct sd_functions { - bool (*probe)(struct sh_sub *sh); + bool accept_packets_in_advance; + bool (*supports_format)(const char *format); int (*init)(struct sh_sub *sh, struct osd_state *osd); void (*decode)(struct sh_sub *sh, struct osd_state *osd, void *data, int data_len, double pts, double duration); void (*get_bitmaps)(struct sh_sub *sh, struct osd_state *osd, struct mp_osd_res dim, double pts, struct sub_bitmaps *res); + char *(*get_text)(struct sh_sub *sh, struct osd_state *osd, double pts); void (*reset)(struct sh_sub *sh, struct osd_state *osd); void (*switch_off)(struct sh_sub *sh, struct osd_state *osd); void (*uninit)(struct sh_sub *sh); diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 8d17835809..1501f5da54 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -17,10 +17,11 @@ */ #include -#include #include #include +#include + #include "talloc.h" #include "core/options.h" @@ -39,11 +40,12 @@ struct sd_ass_priv { bool incomplete_event; struct sub_bitmap *parts; bool flush_on_seek; + char last_text[500]; }; -static bool probe(struct sh_sub *sh) +static bool supports_format(const char *format) { - return is_text_sub(sh->gsh->codec); + return is_text_sub(format); } static void free_last_event(ASS_Track *track) @@ -163,6 +165,89 @@ static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd, talloc_steal(ctx, ctx->parts); } +struct buf { + char *start; + int size; + int len; +}; + +static void append(struct buf *b, char c) +{ + if (b->len < b->size) { + b->start[b->len] = c; + b->len++; + } +} + +static void ass_to_plaintext(struct buf *b, const char *in) +{ + bool in_tag = false; + bool in_drawing = false; + while (*in) { + if (in_tag) { + if (in[0] == '}') { + in += 1; + in_tag = false; + } else if (in[0] == '\\' && in[1] == 'p') { + in += 2; + // skip text between \pN and \p0 tags + if (in[0] == '0') { + in_drawing = false; + } else if (in[0] >= '1' && in[0] <= '9') { + in_drawing = true; + } + } else { + in += 1; + } + } else { + if (in[0] == '\\' && (in[1] == 'N' || in[1] == 'n')) { + in += 2; + append(b, '\n'); + } else if (in[0] == '\\' && in[1] == 'h') { + in += 2; + append(b, ' '); + } else if (in[0] == '{') { + in += 1; + in_tag = true; + } else { + if (!in_drawing) + append(b, in[0]); + in += 1; + } + } + } +} + +static char *get_text(struct sh_sub *sh, struct osd_state *osd, double pts) +{ + struct sd_ass_priv *ctx = sh->context; + ASS_Track *track = ctx->ass_track; + + if (pts == MP_NOPTS_VALUE) + return NULL; + + struct buf b = {ctx->last_text, sizeof(ctx->last_text) - 1}; + + for (int i = 0; i < track->n_events; ++i) { + ASS_Event *event = track->events + i; + double start = event->Start / 1000.0; + double end = (event->Start + event->Duration) / 1000.0; + if (pts >= start && pts < end) { + if (event->Text) { + ass_to_plaintext(&b, event->Text); + append(&b, '\n'); + } + } + } + + b.start[b.len] = '\0'; + + if (b.len > 0 && b.start[b.len - 1] == '\n') + b.start[b.len - 1] = '\0'; + + return ctx->last_text; +} + static void reset(struct sh_sub *sh, struct osd_state *osd) { struct sd_ass_priv *ctx = sh->context; @@ -183,10 +268,12 @@ static void uninit(struct sh_sub *sh) } const struct sd_functions sd_ass = { - .probe = probe, + .accept_packets_in_advance = true, + .supports_format = supports_format, .init = init, .decode = decode, .get_bitmaps = get_bitmaps, + .get_text = get_text, .reset = reset, .switch_off = reset, .uninit = uninit, @@ -199,20 +286,19 @@ static int sd_ass_track_destructor(void *ptr) } struct sh_sub *sd_ass_create_from_track(struct ass_track *track, - bool vsfilter_aspect, - struct MPOpts *opts) + const char *codec, struct MPOpts *opts) { struct sh_sub *sh = talloc(NULL, struct sh_sub); talloc_set_destructor(sh, sd_ass_track_destructor); *sh = (struct sh_sub) { .opts = opts, .gsh = talloc_struct(sh, struct sh_stream, { - .codec = "ass", + .codec = codec, }), .sd_driver = &sd_ass, .context = talloc_struct(sh, struct sd_ass_priv, { .ass_track = track, - .vsfilter_aspect = vsfilter_aspect, + .vsfilter_aspect = is_ass_sub(codec), }), .initialized = true, }; diff --git a/sub/sd_lavc.c b/sub/sd_lavc.c index 1665e36749..e067da6a43 100644 --- a/sub/sd_lavc.c +++ b/sub/sd_lavc.c @@ -41,9 +41,9 @@ struct sd_lavc_priv { double endpts; }; -static bool probe(struct sh_sub *sh) +static bool supports_format(const char *format) { - enum AVCodecID cid = mp_codec_to_av_codec_id(sh->gsh->codec); + enum AVCodecID cid = mp_codec_to_av_codec_id(format); // Supported codecs must be known to decode to paletted bitmaps switch (cid) { case AV_CODEC_ID_DVB_SUBTITLE: @@ -241,7 +241,7 @@ static void uninit(struct sh_sub *sh) } const struct sd_functions sd_lavc = { - .probe = probe, + .supports_format = supports_format, .init = init, .decode = decode, .get_bitmaps = get_bitmaps, diff --git a/sub/sub.c b/sub/sub.c index 7111f39434..4e5420627c 100644 --- a/sub/sub.c +++ b/sub/sub.c @@ -45,8 +45,6 @@ int sub_pos=100; int sub_visibility=1; -subtitle* vo_sub=NULL; - float sub_delay = 0; float sub_fps = 0; @@ -103,6 +101,7 @@ struct osd_state *osd_create(struct MPOpts *opts, struct ass_library *asslib) .opts = opts, .ass_library = asslib, .osd_text = talloc_strdup(osd, ""), + .sub_text = talloc_strdup(osd, ""), .progbar_type = -1, }; @@ -117,7 +116,7 @@ struct osd_state *osd_create(struct MPOpts *opts, struct ass_library *asslib) osd->objs[OSDTYPE_SPU]->is_sub = true; // spudec.c osd->objs[OSDTYPE_SUB]->is_sub = true; // dec_sub.c - osd->objs[OSDTYPE_SUBTITLE]->is_sub = true; // osd_libass.c + osd->objs[OSDTYPE_SUBTEXT]->is_sub = true; // osd_libass.c osd_init_backend(osd); global_osd = osd; @@ -133,15 +132,27 @@ void osd_free(struct osd_state *osd) global_osd = NULL; } -void osd_set_text(struct osd_state *osd, const char *text) +static bool set_text(void *talloc_ctx, char **var, const char *text) { if (!text) text = ""; - if (strcmp(osd->osd_text, text) == 0) - return; - talloc_free(osd->osd_text); - osd->osd_text = talloc_strdup(osd, text); - vo_osd_changed(OSDTYPE_OSD); + if (strcmp(*var, text) == 0) + return true; + talloc_free(*var); + *var = talloc_strdup(talloc_ctx, text); + return false; +} + +void osd_set_text(struct osd_state *osd, const char *text) +{ + if (!set_text(osd, &osd->osd_text, text)) + vo_osd_changed(OSDTYPE_OSD); +} + +void osd_set_sub(struct osd_state *osd, const char *text) +{ + if (!set_text(osd, &osd->sub_text, text)) + vo_osd_changed(OSDTYPE_SUBTEXT); } static bool spu_visible(struct osd_state *osd, struct osd_object *obj) @@ -172,10 +183,12 @@ static void render_object(struct osd_state *osd, struct osd_object *obj, if (spu_visible(osd, obj)) spudec_get_indexed(vo_spudec, &obj->vo_res, out_imgs); } else if (obj->type == OSDTYPE_SUB) { - double sub_pts = video_pts; - if (sub_pts != MP_NOPTS_VALUE) - sub_pts += sub_delay - osd->sub_offset; - sub_get_bitmaps(osd, obj->vo_res, sub_pts, out_imgs); + if (osd->render_bitmap_subs) { + double sub_pts = video_pts; + if (sub_pts != MP_NOPTS_VALUE) + sub_pts -= osd->sub_offset; + sub_get_bitmaps(osd, obj->vo_res, sub_pts, out_imgs); + } } else { osd_object_get_bitmaps(osd, obj, out_imgs); } diff --git a/sub/sub.h b/sub/sub.h index 779d46c869..a922d2b162 100644 --- a/sub/sub.h +++ b/sub/sub.h @@ -85,7 +85,7 @@ struct mp_osd_res { enum mp_osdtype { OSDTYPE_SUB, - OSDTYPE_SUBTITLE, + OSDTYPE_SUBTEXT, OSDTYPE_SPU, OSDTYPE_PROGBAR, @@ -126,11 +126,14 @@ struct osd_state { double vo_pts; bool render_subs_in_filter; + bool render_bitmap_subs; bool want_redraw; // OSDTYPE_OSD char *osd_text; + // OSDTYPE_SUBTEXT + char *sub_text; // OSDTYPE_PROGBAR int progbar_type; // <0: disabled, 1-255: symbol, else: no symbol float progbar_value; // range 0.0-1.0 @@ -149,8 +152,6 @@ struct osd_state { struct ass_library *osd_ass_library; }; -extern struct subtitle* vo_sub; - extern void* vo_spudec; extern void* vo_vobsub; @@ -206,6 +207,7 @@ extern float sub_fps; struct osd_state *osd_create(struct MPOpts *opts, struct ass_library *asslib); void osd_set_text(struct osd_state *osd, const char *text); +void osd_set_sub(struct osd_state *osd, const char *text); void vo_osd_changed(int new_value); void osd_changed_all(struct osd_state *osd); void osd_free(struct osd_state *osd); diff --git a/sub/subreader.c b/sub/subreader.c index 090cd0a8b4..365f8aa532 100644 --- a/sub/subreader.c +++ b/sub/subreader.c @@ -1684,7 +1684,7 @@ if ((suboverlap_enabled == 2) || if (return_sub == NULL) return NULL; subt_data = talloc_zero(NULL, sub_data); talloc_set_destructor(subt_data, sub_destroy); - subt_data->codec = srp->name; + subt_data->codec = "text"; //srp->name; subt_data->filename = strdup(filename); subt_data->sub_uses_time = uses_time; subt_data->sub_num = sub_num; @@ -1704,103 +1704,3 @@ static int sub_destroy(void *ptr) free( subd->filename ); return 0; } - -#define MAX_SUBLINE 512 -/** - * \brief parse text and append it to subtitle in sub - * \param sub subtitle struct to add text to - * \param txt text to parse - * \param len length of text in txt - * \param endpts pts at which this subtitle text should be removed again - * - * <> and {} are interpreted as comment delimiters, "\n", "\N", '\n', '\r' - * and '\0' are interpreted as newlines, duplicate, leading and trailing - * newlines are ignored. - */ -void sub_add_text(subtitle *sub, const char *txt, int len, double endpts) { - int comment = 0; - int double_newline = 1; // ignore newlines at the beginning - int i, pos; - char *buf; - if (sub->lines >= SUB_MAX_TEXT) return; - pos = 0; - buf = malloc(MAX_SUBLINE + 1); - sub->text[sub->lines] = buf; - sub->endpts[sub->lines] = endpts; - for (i = 0; i < len && pos < MAX_SUBLINE; i++) { - char c = txt[i]; - if (c == '<') comment |= 1; - if (c == '{') comment |= 2; - if (comment) { - if (c == '}') comment &= ~2; - if (c == '>') comment &= ~1; - continue; - } - if (pos == MAX_SUBLINE - 1) { - i--; - c = 0; - } - if (c == '\\' && i + 1 < len) { - c = txt[++i]; - if (c == 'n' || c == 'N') c = 0; - } - if (c == '\n' || c == '\r') c = 0; - if (c) { - double_newline = 0; - buf[pos++] = c; - } else if (!double_newline) { - if (sub->lines >= SUB_MAX_TEXT - 1) { - mp_msg(MSGT_VO, MSGL_WARN, "Too many subtitle lines\n"); - break; - } - double_newline = 1; - buf[pos] = 0; - sub->lines++; - pos = 0; - buf = malloc(MAX_SUBLINE + 1); - sub->text[sub->lines] = buf; - sub->endpts[sub->lines] = endpts; - } - } - buf[pos] = 0; - if (sub->lines < SUB_MAX_TEXT && - strlen(sub->text[sub->lines])) - sub->lines++; - if (sub->lines > 1 && - strcmp(sub->text[sub->lines-1], sub->text[sub->lines-2]) == 0) { - // remove duplicate lines. These can happen with some - // "clever" ASS effects. - sub->lines--; - sub->endpts[sub->lines-1] = - FFMAX(sub->endpts[sub->lines-1], - sub->endpts[sub->lines]); - free(sub->text[sub->lines]); - } -} - -/** - * \brief remove outdated subtitle lines. - * \param sub subtitle struct to modify - * \param pts current pts. All lines with endpts <= this will be removed. - * Use MP_NOPTS_VALUE to remove all lines - * \return 1 if sub was modified, 0 otherwise. - */ -int sub_clear_text(subtitle *sub, double pts) { - int i = 0; - int changed = 0; - while (i < sub->lines) { - double endpts = sub->endpts[i]; - if (pts == MP_NOPTS_VALUE || (endpts != MP_NOPTS_VALUE && pts >= endpts)) { - int j; - free(sub->text[i]); - for (j = i + 1; j < sub->lines; j++) { - sub->text[j - 1] = sub->text[j]; - sub->endpts[j - 1] = sub->endpts[j]; - } - sub->lines--; - changed = 1; - } else - i++; - } - return changed; -} diff --git a/sub/subreader.h b/sub/subreader.h index 7a2316bdf1..ab4763cefe 100644 --- a/sub/subreader.h +++ b/sub/subreader.h @@ -67,7 +67,6 @@ typedef struct subtitle { unsigned long end; char *text[SUB_MAX_TEXT]; - double endpts[SUB_MAX_TEXT]; unsigned char alignment; } subtitle; @@ -92,10 +91,5 @@ void subcp_close (void); /* for demux_ogg.c */ const char* guess_buffer_cp(unsigned char* buffer, int buflen, const char *preferred_language, const char *fallback); const char* guess_cp(struct stream *st, const char *preferred_language, const char *fallback); #endif -struct MPContext; -void find_sub(struct MPContext *mpctx, sub_data* subd,int key); -void step_sub(sub_data *subd, float pts, int movement); -void sub_add_text(subtitle *sub, const char *txt, int len, double endpts); -int sub_clear_text(subtitle *sub, double pts); #endif /* MPLAYER_SUBREADER_H */