From b44202b69fc4a1dd1659f7940c5f8846d316e0ff Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 28 Apr 2013 21:12:11 +0200 Subject: [PATCH 01/28] sub: redo how -no-ass is handled The -no-ass switch used to disable any use of libass for text subtitles. This is not really the case anymore, because libass is now always involved when rendering text. The only remaining use of -no-ass is disabling styling or showing subtitles on the terminal. On the other hand, the old subtitle rendering path is a big reason why the subtitle code is still a big mess with an awful number of obscure special cases. In order to simplify it, remove the old subtitle rendering code, and always go through sd_ass.c. Basically, we use ASS_Track as central data structure for storing text subtitles instead of struct sub_data. This also makes libass mandatory for all text subs, even if they are printed to the terminal in -no-video mode. (We could add something like sd_text to avoid this, but it's not worth the trouble.) struct sub_data and subreader.c are still around, even its ASS/SSA reader. But struct sub_data is freed right after converting it to ASS_Track. The internal ASS reader actually can handle some obscure cases libass can't, like files encoded in UTF-16. --- Makefile | 1 - core/command.c | 20 ++---- core/mp_core.h | 6 -- core/mplayer.c | 145 ++++++++++++++------------------------- core/mplayer.h | 3 - sub/dec_sub.c | 24 ++++++- sub/dec_sub.h | 5 +- sub/find_sub.c | 174 ----------------------------------------------- sub/osd_libass.c | 12 +--- sub/sd.h | 4 +- sub/sd_ass.c | 102 ++++++++++++++++++++++++--- sub/sd_lavc.c | 6 +- sub/sub.c | 39 +++++++---- sub/sub.h | 8 ++- sub/subreader.c | 102 +-------------------------- sub/subreader.h | 6 -- 16 files changed, 213 insertions(+), 444 deletions(-) delete mode 100644 sub/find_sub.c 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 */ From 38bd757e4fd4d87441cdc20777c0cc2eaab85db8 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 28 Apr 2013 21:12:18 +0200 Subject: [PATCH 02/28] sub: remove check_duplicate_plaintext_event() This was once needed to handle subtitle packages coming from a demuxer, where seeking back might repeat previous events. This doesn't happen anymore, and this code is used to convert complete files. So if there are any duplicate lines, they must have been duplicated in the file, and the old subtitle renderer would have shown them twice as well. Today checking for duplicate events happens in sd_ass.c (and has been for a while). There's no reason to keep this code, and it actually causes trouble. Loading big subtitle files is extremely slow because this makes adding n subtitles O(n^2). --- sub/ass_mp.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/sub/ass_mp.c b/sub/ass_mp.c index 497e307953..f00bf22428 100644 --- a/sub/ass_mp.c +++ b/sub/ass_mp.c @@ -102,19 +102,6 @@ ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts) return track; } -static int check_duplicate_plaintext_event(ASS_Track *track) -{ - int i; - ASS_Event *evt = track->events + track->n_events - 1; - - for (i = 0; i < track->n_events - 1; ++i) // ignoring last event, it is the one we are comparing with - if (track->events[i].Start == evt->Start && - track->events[i].Duration == evt->Duration && - strcmp(track->events[i].Text, evt->Text) == 0) - return 1; - return 0; -} - /** * \brief Convert subtitle to ASS_Events for the given track * \param track track @@ -159,12 +146,6 @@ static int ass_process_subtitle(ASS_Track *track, subtitle *sub) p -= 2; // remove last "\N" *p = 0; - if (check_duplicate_plaintext_event(track)) { - ass_free_event(track, eid); - track->n_events--; - return -1; - } - mp_msg(MSGT_ASS, MSGL_V, "plaintext event at %" PRId64 ", +%" PRId64 ": %s \n", (int64_t) event->Start, (int64_t) event->Duration, event->Text); From 724f576211c1ea07e0423ac8a9c0e4f273c452fb Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 29 Apr 2013 01:11:50 +0200 Subject: [PATCH 03/28] sub: use DVD PTS fallback code in normal sub decoding path It appears demux_mpg doesn't output timestamps for subtitles. The vobsub code handled this by doing its own PTS calculations. This code is absent from the normal subtitle decoder path. Copy this code into the normal path, so that we can unify the subtitle decoder paths in a later commit. Decoding subtitles with sd_lavc when playing DVD with demux_mpg still doesn't work. --- core/mplayer.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/mplayer.c b/core/mplayer.c index 602d1f0616..bb6a0872cc 100644 --- a/core/mplayer.c +++ b/core/mplayer.c @@ -1938,6 +1938,20 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl) while (d_sub->first) { double subpts_s = ds_get_next_pts(d_sub); + if (subpts_s == MP_NOPTS_VALUE) { + // Try old method of getting PTS. This is only needed in the + // DVD playback case with demux_mpg. + // 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 + subpts_s = d_sub->pts; + else + subpts_s = curpts_s; + } if (subpts_s > curpts_s) { mp_dbg(MSGT_CPLAYER, MSGL_DBG2, "Sub early: c_pts=%5.3f s_pts=%5.3f\n", From 26842806431a1d21e3c3c430994cd6901e36a08e Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 29 Apr 2013 01:13:22 +0200 Subject: [PATCH 04/28] 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. --- Makefile | 1 + core/command.c | 23 +-------- core/mp_core.h | 2 - core/mplayer.c | 127 +++++++--------------------------------------- demux/demux_mpg.c | 2 +- sub/dec_sub.c | 28 ++++++---- sub/sd_ass.c | 1 - sub/sd_lavc.c | 8 +++ sub/sd_spu.c | 99 ++++++++++++++++++++++++++++++++++++ sub/spudec.c | 31 +++++------ sub/spudec.h | 4 +- sub/sub.c | 16 +----- sub/sub.h | 8 +-- 13 files changed, 171 insertions(+), 179 deletions(-) create mode 100644 sub/sd_spu.c diff --git a/Makefile b/Makefile index f7b94f44e9..e91564703e 100644 --- a/Makefile +++ b/Makefile @@ -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 \ diff --git a/core/command.c b/core/command.c index 7eb36be5f3..4e82ceccd8 100644 --- a/core/command.c +++ b/core/command.c @@ -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), diff --git a/core/mp_core.h b/core/mp_core.h index 665588c5c0..db6bc570e1 100644 --- a/core/mp_core.h +++ b/core/mp_core.h @@ -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); diff --git a/core/mplayer.c b/core/mplayer.c index bb6a0872cc..c596aa8504 100644 --- a/core/mplayer.c +++ b/core/mplayer.c @@ -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. diff --git a/demux/demux_mpg.c b/demux/demux_mpg.c index b30fb8e6e1..d47b3afd86 100644 --- a/demux/demux_mpg.c +++ b/demux/demux_mpg.c @@ -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); } diff --git a/sub/dec_sub.c b/sub/dec_sub.c index 3de7d1223d..36ba25edd2 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -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; diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 1501f5da54..3dfbbd05fb 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -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), diff --git a/sub/sd_lavc.c b/sub/sd_lavc.c index e067da6a43..4e1d80d8ce 100644 --- a/sub/sd_lavc.c +++ b/sub/sd_lavc.c @@ -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) { diff --git a/sub/sd_spu.c b/sub/sd_spu.c new file mode 100644 index 0000000000..fc4e4701dc --- /dev/null +++ b/sub/sd_spu.c @@ -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 . + */ + +#include +#include + +#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, +}; diff --git a/sub/spudec.c b/sub/spudec.c index 59c3058251..87cd46bdd7 100644 --- a/sub/spudec.c +++ b/sub/spudec.c @@ -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; diff --git a/sub/spudec.h b/sub/spudec.h index fa395798ac..3a44bd1e2e 100644 --- a/sub/spudec.h +++ b/sub/spudec.h @@ -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 */ diff --git a/sub/sub.c b/sub/sub.c index 4e5420627c..d26ce17551 100644 --- a/sub/sub.c +++ b/sub/sub.c @@ -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) diff --git a/sub/sub.h b/sub/sub.h index a922d2b162..6b15cf90c0 100644 --- a/sub/sub.h +++ b/sub/sub.h @@ -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 From f429b4eaa89d9d752b2701582ebc287265f20450 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 29 Apr 2013 01:19:56 +0200 Subject: [PATCH 05/28] spudec: restore --sub-forced-only support This was broken with 84829a4 "Merge branch 'osd_changes' into master". The new OSD/subtitle code never respected the --sub-forced-only option, and the old code containing the code for this was removed in fd5c4a1. --- sub/spudec.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sub/spudec.c b/sub/spudec.c index 87cd46bdd7..07dbb3af07 100644 --- a/sub/spudec.c +++ b/sub/spudec.c @@ -629,6 +629,8 @@ int spudec_visible(void *this){ spu->now_pts < spu->end_pts && spu->height > 0); // printf("spu visible: %d \n",ret); + if ((spu->forced_subs_only) && !(spu->is_forced_sub)) + ret = 0; return ret; } From fd02f0f4d88371c728aff3b4487974118ba4d014 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 29 Apr 2013 01:39:50 +0200 Subject: [PATCH 06/28] options: add --no-sub-visibility for symmetry Not really useful, but for symmetry with the sub-visibility property (mapped to the 'v' key by default). --- DOCS/man/en/options.rst | 5 ++++- core/cfg-mplayer.h | 1 + core/command.c | 25 +------------------------ 3 files changed, 6 insertions(+), 25 deletions(-) diff --git a/DOCS/man/en/options.rst b/DOCS/man/en/options.rst index d98fe960e6..a609e5f472 100644 --- a/DOCS/man/en/options.rst +++ b/DOCS/man/en/options.rst @@ -1284,7 +1284,10 @@ See ``quit_watch_later`` input command. --no-sub - Disables display of internal and external subtitles. + Don't select any subtitle when the file is loaded. + +--no-sub-visibility + Disable display of subtitles, but still select and decode them. --no-video Do not play video. With some demuxers this may not work. In those cases diff --git a/core/cfg-mplayer.h b/core/cfg-mplayer.h index 5a2c32e0d0..529972766b 100644 --- a/core/cfg-mplayer.h +++ b/core/cfg-mplayer.h @@ -476,6 +476,7 @@ const m_option_t common_opts[] = { {"sub-delay", &sub_delay, CONF_TYPE_FLOAT, 0, 0.0, 10.0, NULL}, {"subfps", &sub_fps, CONF_TYPE_FLOAT, 0, 0.0, 10.0, NULL}, OPT_FLAG("autosub", sub_auto, 0), + OPT_FLAG("sub-visibility", sub_visibility, 0), OPT_FLAG("sub-forced-only", forced_subs_only, 0), // enable Closed Captioning display {"overlapsub", &suboverlap_enabled, CONF_TYPE_FLAG, 0, 0, 2, NULL}, diff --git a/core/command.c b/core/command.c index 4e82ceccd8..5fa914fdf7 100644 --- a/core/command.c +++ b/core/command.c @@ -1294,28 +1294,6 @@ static int mp_property_sub_pos(m_option_t *prop, int action, void *arg, return property_osd_helper(prop, action, arg, mpctx); } -/// Subtitle visibility (RW) -static int mp_property_sub_visibility(m_option_t *prop, int action, - void *arg, MPContext *mpctx) -{ - struct MPOpts *opts = &mpctx->opts; - - if (!mpctx->sh_video) - return M_PROPERTY_UNAVAILABLE; - - switch (action) { - case M_PROPERTY_SET: - opts->sub_visibility = *(int *)arg; - osd_changed_all(mpctx->osd); - return M_PROPERTY_OK; - case M_PROPERTY_GET: - *(int *)arg = opts->sub_visibility; - return M_PROPERTY_OK; - } - return M_PROPERTY_NOT_IMPLEMENTED; -} - - #ifdef CONFIG_TV static tvi_handle_t *get_tvh(struct MPContext *mpctx) @@ -1493,8 +1471,7 @@ static const m_option_t mp_properties[] = { M_OPTION_PROPERTY_CUSTOM("sid", mp_property_sub), M_OPTION_PROPERTY_CUSTOM("sub-delay", mp_property_sub_delay), 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-visibility", property_osd_helper), M_OPTION_PROPERTY_CUSTOM("sub-forced-only", property_osd_helper), M_OPTION_PROPERTY_CUSTOM("sub-scale", property_osd_helper), #ifdef CONFIG_ASS From 28116b8a799078b3c6b3b559ed4463669c79cf0e Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 29 Apr 2013 01:49:20 +0200 Subject: [PATCH 07/28] sub: remove some global variables --- core/cfg-mplayer.h | 16 +++--- core/command.c | 12 ++-- core/defaultopts.c | 2 + core/mplayer.c | 11 ++-- core/options.h | 10 +++- sub/ass_mp.c | 4 +- sub/find_subfiles.c | 9 ++- sub/osd_libass.c | 2 +- sub/sub.c | 19 ++----- sub/sub.h | 9 +-- sub/subreader.c | 134 +++++++++++++++++++++----------------------- sub/subreader.h | 17 ------ 12 files changed, 108 insertions(+), 137 deletions(-) diff --git a/core/cfg-mplayer.h b/core/cfg-mplayer.h index 529972766b..c29d4c4060 100644 --- a/core/cfg-mplayer.h +++ b/core/cfg-mplayer.h @@ -472,18 +472,18 @@ const m_option_t common_opts[] = { OPT_STRINGLIST("sub", sub_name, 0), OPT_PATHLIST("sub-paths", sub_paths, 0), - {"subcp", &sub_cp, CONF_TYPE_STRING, 0, 0, 0, NULL}, - {"sub-delay", &sub_delay, CONF_TYPE_FLOAT, 0, 0.0, 10.0, NULL}, - {"subfps", &sub_fps, CONF_TYPE_FLOAT, 0, 0.0, 10.0, NULL}, + OPT_STRING("subcp", sub_cp, 0), + OPT_FLOAT("sub-delay", sub_delay, 0), + OPT_FLOAT("subfps", sub_fps, 0), OPT_FLAG("autosub", sub_auto, 0), OPT_FLAG("sub-visibility", sub_visibility, 0), OPT_FLAG("sub-forced-only", forced_subs_only, 0), // enable Closed Captioning display - {"overlapsub", &suboverlap_enabled, CONF_TYPE_FLAG, 0, 0, 2, NULL}, - {"sub-no-text-pp", &sub_no_text_pp, CONF_TYPE_FLAG, 0, 0, 1, NULL}, - {"autosub-match", &sub_match_fuzziness, CONF_TYPE_CHOICE, 0, - M_CHOICES(({"exact", 0}, {"fuzzy", 1}, {"all", 2}))}, - {"sub-pos", &sub_pos, CONF_TYPE_INT, CONF_RANGE, 0, 100, NULL}, + OPT_FLAG_CONSTANTS("overlapsub", suboverlap_enabled, 0, 0, 2), + OPT_FLAG_STORE("sub-no-text-pp", sub_no_text_pp, 0, 1), + OPT_CHOICE("autosub-match", sub_match_fuzziness, 0, + ({"exact", 0}, {"fuzzy", 1}, {"all", 2})), + OPT_INTRANGE("sub-pos", sub_pos, 0, 0, 100), OPT_FLOATRANGE("sub-gauss", sub_gauss, 0, 0.0, 3.0), OPT_FLAG("sub-gray", sub_gray, 0), OPT_FLAG("ass", ass_enabled, 0), diff --git a/core/command.c b/core/command.c index 5fa914fdf7..7971180553 100644 --- a/core/command.c +++ b/core/command.c @@ -1272,11 +1272,12 @@ static int mp_property_sub(m_option_t *prop, int action, void *arg, static int mp_property_sub_delay(m_option_t *prop, int action, void *arg, MPContext *mpctx) { + struct MPOpts *opts = &mpctx->opts; if (!mpctx->sh_video) return M_PROPERTY_UNAVAILABLE; switch (action) { case M_PROPERTY_PRINT: - *(char **)arg = format_delay(sub_delay); + *(char **)arg = format_delay(opts->sub_delay); return M_PROPERTY_OK; } return mp_property_generic_option(prop, action, arg, mpctx); @@ -1285,10 +1286,11 @@ static int mp_property_sub_delay(m_option_t *prop, int action, void *arg, static int mp_property_sub_pos(m_option_t *prop, int action, void *arg, MPContext *mpctx) { + struct MPOpts *opts = &mpctx->opts; if (!mpctx->sh_video) return M_PROPERTY_UNAVAILABLE; if (action == M_PROPERTY_PRINT) { - *(char **)arg = talloc_asprintf(NULL, "%d/100", sub_pos); + *(char **)arg = talloc_asprintf(NULL, "%d/100", opts->sub_pos); return M_PROPERTY_OK; } return property_osd_helper(prop, action, arg, mpctx); @@ -1954,9 +1956,9 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) struct ass_track *ass_track = sub_get_ass_track(mpctx->osd); if (ass_track) { 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.; + "Sub delay: %d ms", ROUND(opts->sub_delay * 1000)); + double cur = (mpctx->video_pts + opts->sub_delay) * 1000 + .5; + opts->sub_delay += ass_step_sub(ass_track, cur, movement) / 1000.; } } #endif diff --git a/core/defaultopts.c b/core/defaultopts.c index c4e2a0fa98..93dad1e624 100644 --- a/core/defaultopts.c +++ b/core/defaultopts.c @@ -74,6 +74,7 @@ void set_default_mplayer_options(struct MPOpts *opts) .sub_id = -1, .audio_display = 1, .sub_visibility = 1, + .sub_pos = 100, .extension_parsing = 1, .audio_output_channels = MP_CHMAP_INIT_STEREO, .audio_output_format = -1, // AF_FORMAT_UNKNOWN @@ -89,6 +90,7 @@ void set_default_mplayer_options(struct MPOpts *opts) .ass_vsfilter_aspect_compat = 1, .ass_style_override = 1, .use_embedded_fonts = 1, + .suboverlap_enabled = 1, .hwdec_codecs = "all", diff --git a/core/mplayer.c b/core/mplayer.c index c596aa8504..c832aa1fcb 100644 --- a/core/mplayer.c +++ b/core/mplayer.c @@ -1056,7 +1056,7 @@ struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename, // the weird special-cases. #ifdef CONFIG_ASS if (opts->ass_enabled) { - asst = mp_ass_read_stream(mpctx->ass_library, filename, sub_cp); + asst = mp_ass_read_stream(mpctx->ass_library, filename, opts->sub_cp); codec = "ass"; } if (!asst) { @@ -1389,7 +1389,7 @@ static mp_osd_msg_t *get_osd_msg(struct MPContext *mpctx) if (mpctx->osd_visible && now >= mpctx->osd_visible) { mpctx->osd_visible = 0; mpctx->osd->progbar_type = -1; // disable - vo_osd_changed(OSDTYPE_PROGBAR); + osd_changed(mpctx->osd, OSDTYPE_PROGBAR); } if (mpctx->osd_function_visible && now >= mpctx->osd_function_visible) { mpctx->osd_function_visible = 0; @@ -1448,7 +1448,7 @@ void set_osd_bar(struct MPContext *mpctx, int type, const char *name, mpctx->osd->progbar_type = type; mpctx->osd->progbar_value = (val - min) / (max - min); mpctx->osd->progbar_num_stops = 0; - vo_osd_changed(OSDTYPE_PROGBAR); + osd_changed(mpctx->osd, OSDTYPE_PROGBAR); return; } @@ -1465,7 +1465,7 @@ static void update_osd_bar(struct MPContext *mpctx, int type, 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); + osd_changed(mpctx->osd, OSDTYPE_PROGBAR); } } } @@ -1831,6 +1831,7 @@ static void reset_subtitles(struct MPContext *mpctx) static void update_subtitles(struct MPContext *mpctx, double refpts_tl) { + struct MPOpts *opts = &mpctx->opts; struct sh_sub *sh_sub = mpctx->sh_sub; struct demux_stream *d_sub = sh_sub ? sh_sub->ds : NULL; unsigned char *packet = NULL; @@ -1843,7 +1844,7 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl) double video_offset = track->under_timeline ? mpctx->video_offset : 0; - mpctx->osd->sub_offset = video_offset - sub_delay; + mpctx->osd->sub_offset = video_offset - opts->sub_delay; double curpts_s = refpts_tl - mpctx->osd->sub_offset; double refpts_s = refpts_tl - video_offset; diff --git a/core/options.h b/core/options.h index d870ab9677..31b4bc9136 100644 --- a/core/options.h +++ b/core/options.h @@ -83,7 +83,6 @@ typedef struct MPOpts { int osd_level; int osd_duration; int osd_fractions; - char *vobsub_name; int untimed; char *stream_capture; char *stream_dump; @@ -140,9 +139,17 @@ typedef struct MPOpts { char **sub_lang; int audio_display; int sub_visibility; + int sub_pos; + float sub_delay; + float sub_fps; int forced_subs_only; char *quvi_format; + // subreader.c + int suboverlap_enabled; + char *sub_cp; + int sub_no_text_pp; + char *audio_stream; int audio_stream_cache; char *sub_stream; @@ -170,6 +177,7 @@ typedef struct MPOpts { char **sub_name; char **sub_paths; int sub_auto; + int sub_match_fuzziness; int osd_bar_visible; float osd_bar_align_x; float osd_bar_align_y; diff --git a/sub/ass_mp.c b/sub/ass_mp.c index f00bf22428..4be89fb004 100644 --- a/sub/ass_mp.c +++ b/sub/ass_mp.c @@ -84,7 +84,7 @@ ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts) track->WrapStyle = 0; if (opts->ass_styles_file && opts->ass_style_override) - ass_read_styles(track, opts->ass_styles_file, sub_cp); + ass_read_styles(track, opts->ass_styles_file, opts->sub_cp); if (track->n_styles == 0) { track->Kerning = true; @@ -225,7 +225,7 @@ void mp_ass_configure(ASS_Renderer *priv, struct MPOpts *opts, if (opts->ass_style_override) { set_use_margins = opts->ass_use_margins; #if LIBASS_VERSION >= 0x01010000 - set_sub_pos = 100 - sub_pos; + set_sub_pos = 100 - opts->sub_pos; #endif set_line_spacing = opts->ass_line_spacing; set_font_scale = opts->sub_scale; diff --git a/sub/find_subfiles.c b/sub/find_subfiles.c index e77015114b..3519e5c386 100644 --- a/sub/find_subfiles.c +++ b/sub/find_subfiles.c @@ -12,7 +12,6 @@ #include "core/mp_common.h" #include "sub/find_subfiles.h" #include "sub/sub.h" -#include "sub/subreader.h" static struct bstr strip_ext(struct bstr str) { @@ -119,9 +118,9 @@ static void append_dir_subtitles(struct MPOpts *opts, // does it end with a subtitle extension? #ifdef CONFIG_ICONV #ifdef CONFIG_ENCA - int i = (sub_cp && strncasecmp(sub_cp, "enca", 4) != 0) ? 3 : 0; + int i = (opts->sub_cp && strncasecmp(opts->sub_cp, "enca", 4) != 0) ? 3 : 0; #else - int i = sub_cp ? 3 : 0; + int i = opts->sub_cp ? 3 : 0; #endif #else int i = 0; @@ -153,12 +152,12 @@ static void append_dir_subtitles(struct MPOpts *opts, if (!prio && bstrcmp(tmp_fname_trim, f_fname_trim) == 0) prio = 3; // matches the movie name if (!prio && bstr_find(tmp_fname_trim, f_fname_trim) >= 0 - && sub_match_fuzziness >= 1) + && opts->sub_match_fuzziness >= 1) prio = 2; // contains the movie name if (!prio) { // doesn't contain the movie name // don't try in the mplayer subtitle directory - if (!limit_fuzziness && sub_match_fuzziness >= 2) { + if (!limit_fuzziness && opts->sub_match_fuzziness >= 2) { prio = 1; } } diff --git a/sub/osd_libass.c b/sub/osd_libass.c index d8496c2f3b..6733b9b6c2 100644 --- a/sub/osd_libass.c +++ b/sub/osd_libass.c @@ -367,7 +367,7 @@ static void update_sub(struct osd_state *osd, struct osd_object *obj) mp_ass_set_style(style, &font); #if LIBASS_VERSION >= 0x01010000 - ass_set_line_position(osd->osd_render, 100 - sub_pos); + ass_set_line_position(osd->osd_render, 100 - opts->sub_pos); #endif char *escaped_text = mangle_ass(osd->sub_text); diff --git a/sub/sub.c b/sub/sub.c index d26ce17551..9dc5722469 100644 --- a/sub/sub.c +++ b/sub/sub.c @@ -41,12 +41,6 @@ #include "video/mp_image.h" #include "video/mp_image_pool.h" -int sub_pos=100; -int sub_visibility=1; - -float sub_delay = 0; -float sub_fps = 0; - static const struct osd_style_opts osd_style_opts_def = { .font = "Sans", .font_size = 45, @@ -80,8 +74,6 @@ const struct m_sub_options osd_style_conf = { .defaults = &osd_style_opts_def, }; -static struct osd_state *global_osd; - static bool osd_res_equals(struct mp_osd_res a, struct mp_osd_res b) { return a.w == b.w && a.h == b.h && a.ml == b.ml && a.mt == b.mt @@ -114,7 +106,6 @@ struct osd_state *osd_create(struct MPOpts *opts, struct ass_library *asslib) osd->objs[OSDTYPE_SUBTEXT]->is_sub = true; // osd_libass.c osd_init_backend(osd); - global_osd = osd; return osd; } @@ -124,7 +115,6 @@ void osd_free(struct osd_state *osd) return; osd_destroy_backend(osd); talloc_free(osd); - global_osd = NULL; } static bool set_text(void *talloc_ctx, char **var, const char *text) @@ -141,13 +131,13 @@ static bool set_text(void *talloc_ctx, char **var, const char *text) void osd_set_text(struct osd_state *osd, const char *text) { if (!set_text(osd, &osd->osd_text, text)) - vo_osd_changed(OSDTYPE_OSD); + osd_changed(osd, 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); + osd_changed(osd, OSDTYPE_SUBTEXT); } static void render_object(struct osd_state *osd, struct osd_object *obj, @@ -302,9 +292,8 @@ void osd_draw_on_image_p(struct osd_state *osd, struct mp_osd_res res, &draw_on_image, &closure); } -void vo_osd_changed(int new_value) +void osd_changed(struct osd_state *osd, int new_value) { - struct osd_state *osd = global_osd; for (int n = 0; n < MAX_OSD_PARTS; n++) { if (osd->objs[n]->type == new_value) osd->objs[n]->force_redraw = true; @@ -315,5 +304,5 @@ void vo_osd_changed(int new_value) void osd_changed_all(struct osd_state *osd) { for (int n = 0; n < MAX_OSD_PARTS; n++) - vo_osd_changed(n); + osd_changed(osd, n); } diff --git a/sub/sub.h b/sub/sub.h index 6b15cf90c0..fae7202ed5 100644 --- a/sub/sub.h +++ b/sub/sub.h @@ -198,17 +198,10 @@ struct osd_style_opts { extern const struct m_sub_options osd_style_conf; -extern char *sub_cp; -extern int sub_pos; - -extern float sub_delay; -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(struct osd_state *osd, 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 365f8aa532..12da7d8f3b 100644 --- a/sub/subreader.c +++ b/sub/subreader.c @@ -48,42 +48,33 @@ #include #endif -char *sub_cp=NULL; - - -int suboverlap_enabled = 1; - // Parameter struct for the format-specific readline functions struct readline_args { int utf16; struct MPOpts *opts; + + // subtitle reader state used by some formats + + float mpsub_multiplier; + float mpsub_position; + int sub_slacktime; + + /* + Some subtitling formats, namely AQT and Subrip09, define the end of a + subtitle as the beginning of the following. Since currently we read one + subtitle at time, for these format we keep two global *subtitle, + previous_aqt_sub and previous_subrip09_sub, pointing to previous subtitle, + so we can change its end when we read current subtitle starting time. + We use a single global unsigned long, + previous_sub_end, for both (and even future) formats, to store the end of + the previous sub: it is initialized to 0 in sub_read_file and eventually + modified by sub_read_aqt_line or sub_read_subrip09_line. + */ + unsigned long previous_sub_end; }; /* Maximal length of line of a subtitle */ #define LINE_LEN 1000 -static float mpsub_position=0; -static float mpsub_multiplier=1.; -static int sub_slacktime = 20000; //20 sec - -int sub_no_text_pp=0; // 1 => do not apply text post-processing - // like {\...} elimination in SSA format. - -int sub_match_fuzziness=0; // level of sub name matching fuzziness - -/* Use the SUB_* constant defined in the header file */ -int sub_format=SUB_INVALID; -/* - Some subtitling formats, namely AQT and Subrip09, define the end of a - subtitle as the beginning of the following. Since currently we read one - subtitle at time, for these format we keep two global *subtitle, - previous_aqt_sub and previous_subrip09_sub, pointing to previous subtitle, - so we can change its end when we read current subtitle starting time. - We use a single global unsigned long, - previous_sub_end, for both (and even future) formats, to store the end of - the previous sub: it is initialized to 0 in sub_read_file and eventually - modified by sub_read_aqt_line or sub_read_subrip09_line. - */ -unsigned long previous_sub_end; static int eol(char p) { return p=='\r' || p=='\n' || p=='\0'; @@ -145,7 +136,7 @@ static subtitle *sub_read_line_sami(stream_t* st, subtitle *current, case 0: /* find "START=" or "Slacktime:" */ slacktime_s = stristr (s, "Slacktime:"); if (slacktime_s) - sub_slacktime = strtol (slacktime_s+10, NULL, 0) / 10; + args->sub_slacktime = strtol (slacktime_s+10, NULL, 0) / 10; s = stristr (s, "Start="); if (s) { @@ -181,7 +172,7 @@ static subtitle *sub_read_line_sami(stream_t* st, subtitle *current, sami_add_line(current, text, &p); s += 4; } - else if ((*s == '{') && !sub_no_text_pp) { state = 5; ++s; continue; } + else if ((*s == '{') && !args->opts->sub_no_text_pp) { state = 5; ++s; continue; } else if (*s == '<') { state = 4; } else if (!strncasecmp (s, " ", 6)) { *p++ = ' '; s += 6; } else if (*s == '\t') { *p++ = ' '; s++; } @@ -207,7 +198,7 @@ static subtitle *sub_read_line_sami(stream_t* st, subtitle *current, if (s) { s++; state = 3; continue; } break; case 5: /* get rid of {...} text, but read the alignment code */ - if ((*s == '\\') && (*(s + 1) == 'a') && !sub_no_text_pp) { + if ((*s == '\\') && (*(s + 1) == 'a') && !args->opts->sub_no_text_pp) { if (stristr(s, "\\a1") != NULL) { current->alignment = SUB_ALIGNMENT_BOTTOMLEFT; s = s + 3; @@ -256,7 +247,7 @@ static subtitle *sub_read_line_sami(stream_t* st, subtitle *current, // For the last subtitle if (current->end <= 0) { - current->end = current->start + sub_slacktime; + current->end = current->start + args->sub_slacktime; sami_add_line(current, text, &p); } @@ -761,10 +752,10 @@ static subtitle *sub_read_line_mpsub(stream_t *st, subtitle *current, if (!stream_read_line(st, line, LINE_LEN, utf16)) return NULL; } while (sscanf (line, "%f %f", &a, &b) !=2); - mpsub_position += a*mpsub_multiplier; - current->start=(int) mpsub_position; - mpsub_position += b*mpsub_multiplier; - current->end=(int) mpsub_position; + args->mpsub_position += a*args->mpsub_multiplier; + current->start=(int) args->mpsub_position; + args->mpsub_position += b*args->mpsub_multiplier; + current->end=(int) args->mpsub_position; while (num < SUB_MAX_TEXT) { if (!stream_read_line (st, line, LINE_LEN, utf16)) { @@ -805,8 +796,8 @@ retry: break; } - if (!previous_sub_end) - previous_sub_end = (current->start) ? current->start - 1 : 0; + if (!args->previous_sub_end) + args->previous_sub_end = (current->start) ? current->start - 1 : 0; if (!stream_read_line (st, line, LINE_LEN, utf16)) return NULL; @@ -846,8 +837,8 @@ retry: current->start = a1*360000+a2*6000+a3*100; - if (!previous_sub_end) - previous_sub_end = (current->start) ? current->start - 1 : 0; + if (!args->previous_sub_end) + args->previous_sub_end = (current->start) ? current->start - 1 : 0; if (!stream_read_line (st, line, LINE_LEN, utf16)) return NULL; @@ -1109,14 +1100,12 @@ static int sub_autodetect (stream_t* st, int *uses_time, int utf16) { return SUB_INVALID; // too many bad lines } -extern float sub_delay; -extern float sub_fps; - #ifdef CONFIG_ICONV -static iconv_t icdsc = (iconv_t)(-1); +static const char* guess_cp(stream_t *st, const char *preferred_language, const char *fallback); -void subcp_open (stream_t *st) +static iconv_t subcp_open (stream_t *st, const char *sub_cp) { + iconv_t icdsc = (iconv_t)(-1); char *tocp = "UTF-8"; if (sub_cp){ @@ -1139,18 +1128,18 @@ void subcp_open (stream_t *st) } else mp_msg(MSGT_SUBREADER,MSGL_ERR,"SUB: error opening iconv descriptor.\n"); } + return icdsc; } -void subcp_close (void) +static void subcp_close (iconv_t icdsc) { if (icdsc != (iconv_t)(-1)){ (void) iconv_close (icdsc); - icdsc = (iconv_t)(-1); mp_msg(MSGT_SUBREADER,MSGL_V,"SUB: closed iconv descriptor.\n"); } } -subtitle* subcp_recode (subtitle *sub) +static subtitle* subcp_recode (iconv_t icdsc, subtitle *sub) { int l=sub->lines; size_t ileft, oleft; @@ -1184,7 +1173,8 @@ subtitle* subcp_recode (subtitle *sub) } #endif -static void adjust_subs_time(subtitle* sub, float subtime, float fps, int block, +static void adjust_subs_time(subtitle* sub, float subtime, float fps, + float sub_fps, int block, int sub_num, int sub_uses_time) { int n,m; subtitle* nextsub; @@ -1251,7 +1241,7 @@ struct subreader { }; #ifdef CONFIG_ENCA -const char* guess_buffer_cp(unsigned char* buffer, int buflen, const char *preferred_language, const char *fallback) +static const char* guess_buffer_cp(unsigned char* buffer, int buflen, const char *preferred_language, const char *fallback) { const char **languages; size_t langcnt; @@ -1291,7 +1281,7 @@ const char* guess_buffer_cp(unsigned char* buffer, int buflen, const char *prefe } #define MAX_GUESS_BUFFER_SIZE (256*1024) -const char* guess_cp(stream_t *st, const char *preferred_language, const char *fallback) +static const char* guess_cp(stream_t *st, const char *preferred_language, const char *fallback) { size_t buflen; unsigned char *buffer; @@ -1343,7 +1333,7 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts) if(filename==NULL) return NULL; //qnx segfault fd=open_stream (filename, NULL, NULL); if (!fd) return NULL; - sub_format = SUB_INVALID; + int sub_format = SUB_INVALID; for (utf16 = 0; sub_format == SUB_INVALID && utf16 < 3; utf16++) { sub_format=sub_autodetect (fd, &uses_time, utf16); stream_reset(fd); @@ -1351,7 +1341,10 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts) } utf16--; - mpsub_multiplier = (uses_time ? 100.0 : 1.0); + struct readline_args args = {utf16, opts}; + args.sub_slacktime = 20000; //20 sec + args.mpsub_multiplier = (uses_time ? 100.0 : 1.0); + if (sub_format==SUB_INVALID) { mp_msg(MSGT_SUBREADER,MSGL_WARN,"SUB: Could not determine file format\n"); free_stream(fd); @@ -1361,6 +1354,7 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts) mp_msg(MSGT_SUBREADER, MSGL_V, "SUB: Detected subtitle file format: %s\n", srp->name); #ifdef CONFIG_ICONV + iconv_t icdsc = (iconv_t)(-1); { int l,k; k = -1; @@ -1371,7 +1365,7 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts) break; } } - if (k<0) subcp_open(fd); + if (k<0) icdsc = subcp_open(fd, opts->sub_cp); } #endif @@ -1384,22 +1378,22 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts) sub = malloc(sizeof(subtitle)); //This is to deal with those formats (AQT & Subrip) which define the end of a subtitle //as the beginning of the following - previous_sub_end = 0; + args.previous_sub_end = 0; while(1){ if(sub_num>=n_max){ n_max+=16; first=realloc(first,n_max*sizeof(subtitle)); } memset(sub, '\0', sizeof(subtitle)); - sub=srp->read(fd, sub, &(struct readline_args){utf16, opts}); + sub=srp->read(fd, sub, &args); if(!sub) break; // EOF #ifdef CONFIG_ICONV - if (sub!=ERR) sub=subcp_recode(sub); + if (sub!=ERR) sub=subcp_recode(icdsc, sub); #endif if ( sub == ERR ) { #ifdef CONFIG_ICONV - subcp_close(); + subcp_close(icdsc); #endif free(first); free(alloced_sub); @@ -1407,7 +1401,7 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts) return NULL; } // Apply any post processing that needs recoding first - if ((sub!=ERR) && !sub_no_text_pp && srp->post) srp->post(sub); + if ((sub!=ERR) && !args.opts->sub_no_text_pp && srp->post) srp->post(sub); if(!sub_num || (first[sub_num - 1].start <= sub->start)){ first[sub_num].start = sub->start; first[sub_num].end = sub->end; @@ -1416,9 +1410,9 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts) for(i = 0; i < sub->lines; ++i){ first[sub_num].text[i] = sub->text[i]; } - if (previous_sub_end){ - first[sub_num - 1].end = previous_sub_end; - previous_sub_end = 0; + if (args.previous_sub_end){ + first[sub_num - 1].end = args.previous_sub_end; + args.previous_sub_end = 0; } } else { for(j = sub_num - 1; j >= 0; --j){ @@ -1437,10 +1431,10 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts) for(i = 0; i < SUB_MAX_TEXT; ++i){ first[j].text[i] = sub->text[i]; } - if (previous_sub_end){ + if (args.previous_sub_end){ first[j].end = first[j - 1].end; - first[j - 1].end = previous_sub_end; - previous_sub_end = 0; + first[j - 1].end = args.previous_sub_end; + args.previous_sub_end = 0; } break; } @@ -1452,7 +1446,7 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts) free_stream(fd); #ifdef CONFIG_ICONV - subcp_close(); + subcp_close(icdsc); #endif free(alloced_sub); @@ -1469,9 +1463,9 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts) // the user didn't forced no-overlapsub and the format is Jacosub or Ssa. // this is because usually overlapping subtitles are found in these formats, // while in others they are probably result of bad timing -if ((suboverlap_enabled == 2) || - ((suboverlap_enabled) && ((sub_format == SUB_JACOSUB) || (sub_format == SUB_SSA)))) { - adjust_subs_time(first, 6.0, fps, 0, sub_num, uses_time);/*~6 secs AST*/ +if ((opts->suboverlap_enabled == 2) || + ((opts->suboverlap_enabled) && ((sub_format == SUB_JACOSUB) || (sub_format == SUB_SSA)))) { + adjust_subs_time(first, 6.0, fps, opts->sub_fps, 0, sub_num, uses_time);/*~6 secs AST*/ // here we manage overlapping subtitles sub_orig = sub_num; n_first = sub_num; @@ -1678,7 +1672,7 @@ if ((suboverlap_enabled == 2) || return_sub = second; } else { //if(suboverlap_enabled) - adjust_subs_time(first, 6.0, fps, 1, sub_num, uses_time);/*~6 secs AST*/ + adjust_subs_time(first, 6.0, fps, opts->sub_fps, 1, sub_num, uses_time);/*~6 secs AST*/ return_sub = first; } if (return_sub == NULL) return NULL; diff --git a/sub/subreader.h b/sub/subreader.h index ab4763cefe..c62dd5ddd2 100644 --- a/sub/subreader.h +++ b/sub/subreader.h @@ -24,10 +24,6 @@ #include "config.h" -extern int suboverlap_enabled; -extern int sub_no_text_pp; // disable text post-processing -extern int sub_match_fuzziness; - // subtitle formats #define SUB_INVALID -1 #define SUB_MICRODVD 0 @@ -45,9 +41,6 @@ extern int sub_match_fuzziness; #define SUB_JACOSUB 12 #define SUB_MPL2 13 -// One of the SUB_* constant above -extern int sub_format; - #define SUB_MAX_TEXT 12 #define SUB_ALIGNMENT_BOTTOMLEFT 1 #define SUB_ALIGNMENT_BOTTOMCENTER 2 @@ -81,15 +74,5 @@ typedef struct sub_data { struct MPOpts; sub_data* sub_read_file (char *filename, float pts, struct MPOpts *opts); -subtitle* subcp_recode (subtitle *sub); -// enca_fd is the file enca uses to determine the codepage. -// setting to NULL disables enca. -struct stream; -void subcp_open (struct stream *st); /* for demux_ogg.c */ -void subcp_close (void); /* for demux_ogg.c */ -#ifdef CONFIG_ENCA -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 #endif /* MPLAYER_SUBREADER_H */ From f7b9b92179333e5bf399cbb6289b66ed3439445c Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 1 Jun 2013 19:40:09 +0200 Subject: [PATCH 08/28] sub: various minor subtitle related changes Just pushing some code around. --- core/mplayer.c | 21 ++++++++++----------- sub/dec_sub.c | 20 -------------------- sub/dec_sub.h | 4 ---- sub/sd_ass.c | 14 ++++++++++++++ sub/sd_lavc.c | 13 +++++-------- sub/sd_spu.c | 6 ++++++ 6 files changed, 35 insertions(+), 43 deletions(-) diff --git a/core/mplayer.c b/core/mplayer.c index c832aa1fcb..285f19c8ac 100644 --- a/core/mplayer.c +++ b/core/mplayer.c @@ -1833,10 +1833,6 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl) { struct MPOpts *opts = &mpctx->opts; 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; struct track *track = mpctx->current_track[STREAM_SUB]; if (!track) @@ -1849,12 +1845,16 @@ 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; - if (d_sub && sh_sub && sh_sub->active) { + if (sh_sub && sh_sub->active) { + struct demux_stream *d_sub = sh_sub->ds; + const char *type = sh_sub->gsh->codec; bool non_interleaved = is_non_interleaved(mpctx, track); - if (non_interleaved) - ds_get_next_pts(d_sub); - while (d_sub->first) { + while (1) { + if (non_interleaved) + ds_get_next_pts(d_sub); + if (!d_sub->first) + break; double subpts_s = ds_get_next_pts(d_sub); if (subpts_s == MP_NOPTS_VALUE) { // Try old method of getting PTS. This is only needed in the @@ -1882,7 +1882,8 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl) break; } double duration = d_sub->first->duration; - len = ds_get_packet_sub(d_sub, &packet); + unsigned char *packet = NULL; + int len = ds_get_packet_sub(d_sub, &packet); mp_dbg(MSGT_CPLAYER, MSGL_V, "Sub: c_pts=%5.3f s_pts=%5.3f " "duration=%5.3f len=%d\n", curpts_s, subpts_s, duration, len); @@ -1893,8 +1894,6 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl) packet += 2; } sub_decode(sh_sub, mpctx->osd, packet, len, subpts_s, duration); - if (non_interleaved) - ds_get_next_pts(d_sub); } } diff --git a/sub/dec_sub.c b/sub/dec_sub.c index 36ba25edd2..2cc02efb79 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -40,26 +40,6 @@ static const struct sd_functions *sd_list[] = { NULL }; -bool is_text_sub(const char *t) -{ - return t && (is_ass_sub(t) || - strcmp(t, "text") == 0 || - strcmp(t, "subrip") == 0 || - strcmp(t, "mov_text") == 0); -} - -bool is_ass_sub(const char *t) -{ - return t && (strcmp(t, "ass") == 0 || - strcmp(t, "ssa") == 0); -} - -bool is_dvd_sub(const char *t) -{ - return t && (strcmp(t, "dvd_subtitle") == 0 || - strcmp(t, "dvd_subtitle_mpg") == 0); -} - void sub_init(struct sh_sub *sh, struct osd_state *osd) { sh->sd_driver = NULL; diff --git a/sub/dec_sub.h b/sub/dec_sub.h index 52fa05eebc..263df19bc0 100644 --- a/sub/dec_sub.h +++ b/sub/dec_sub.h @@ -10,10 +10,6 @@ struct sh_sub; struct ass_track; struct MPOpts; -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); diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 3dfbbd05fb..02d0ed15bf 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -43,6 +43,20 @@ struct sd_ass_priv { char last_text[500]; }; +static bool is_ass_sub(const char *t) +{ + return t && (strcmp(t, "ass") == 0 || + strcmp(t, "ssa") == 0); +} + +static bool is_text_sub(const char *t) +{ + return t && (is_ass_sub(t) || + strcmp(t, "text") == 0 || + strcmp(t, "subrip") == 0 || + strcmp(t, "mov_text") == 0); +} + static bool supports_format(const char *format) { return is_text_sub(format); diff --git a/sub/sd_lavc.c b/sub/sd_lavc.c index 4e1d80d8ce..4c7dfd12a5 100644 --- a/sub/sd_lavc.c +++ b/sub/sd_lavc.c @@ -43,21 +43,18 @@ 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) { case AV_CODEC_ID_DVB_SUBTITLE: case AV_CODEC_ID_HDMV_PGS_SUBTITLE: case AV_CODEC_ID_XSUB: + // 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) case AV_CODEC_ID_DVD_SUBTITLE: +#endif return true; default: return false; diff --git a/sub/sd_spu.c b/sub/sd_spu.c index fc4e4701dc..8b87b7a0dc 100644 --- a/sub/sd_spu.c +++ b/sub/sd_spu.c @@ -29,6 +29,12 @@ struct sd_spu_priv { void *spudec; }; +static bool is_dvd_sub(const char *t) +{ + return t && (strcmp(t, "dvd_subtitle") == 0 || + strcmp(t, "dvd_subtitle_mpg") == 0); +} + static bool supports_format(const char *format) { return is_dvd_sub(format); From 27d383918a3d63559c85ca96b2162a13234f2abc Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 1 Jun 2013 19:43:11 +0200 Subject: [PATCH 09/28] core: add demux_sub pseudo demuxer Subtitle files are opened in mplayer.c, not using the demuxer infrastructure in general. Pretend that this is not the case (outside of the loading code) by opening a pseudo demuxer that does nothing. One advantage is that the initialization code is now the same, and there's no confusion about what the difference between track->stream, track->sh_sub and mpctx->sh_sub is supposed to be. This is a bit stupid, and it would be much better if there were proper subtitle demuxers (there are many in recent FFmpeg, but not Libav). So for now this is just a transition to a more proper architecture. Look at demux_sub like an artifical limb: it's ugly, but don't hate it - it helps you to get on with your life. --- Makefile | 1 + core/mp_core.h | 9 ++---- core/mplayer.c | 75 ++++++++++++++++++++++++----------------------- demux/demux.c | 19 ++++++++++-- demux/demux.h | 7 +++-- demux/demux_sub.c | 38 ++++++++++++++++++++++++ demux/stheader.h | 1 + sub/dec_sub.h | 3 -- sub/sd_ass.c | 32 ++++---------------- 9 files changed, 108 insertions(+), 77 deletions(-) create mode 100644 demux/demux_sub.c diff --git a/Makefile b/Makefile index e91564703e..6e52253543 100644 --- a/Makefile +++ b/Makefile @@ -205,6 +205,7 @@ SOURCES = talloc.c \ demux/demux_mf.c \ demux/demux_mkv.c \ demux/demux_mpg.c \ + demux/demux_sub.c \ demux/demux_ts.c \ demux/mp3_hdr.c \ demux/parse_es.c \ diff --git a/core/mp_core.h b/core/mp_core.h index db6bc570e1..d7aa42e38d 100644 --- a/core/mp_core.h +++ b/core/mp_core.h @@ -103,12 +103,9 @@ struct track { // Invariant: (!demuxer && !stream) || stream->demuxer == demuxer struct sh_stream *stream; - // NOTE: demuxer subtitles, i.e. if stream!=NULL, do not use the following - // fields. The data is stored in stream->sub this case. - - // 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; + // For external subtitles, which are read fully on init. Do not attempt + // to read packets from them. + bool preloaded; }; enum { diff --git a/core/mplayer.c b/core/mplayer.c index 285f19c8ac..a19788df0b 100644 --- a/core/mplayer.c +++ b/core/mplayer.c @@ -281,8 +281,6 @@ static void print_stream(struct MPContext *mpctx, struct track *t) if (t->title) mp_msg(MSGT_CPLAYER, MSGL_INFO, " '%s'", t->title); const char *codec = s ? s->codec : NULL; - if (!codec && t->sh_sub) // external subs hack - codec = t->sh_sub->gsh->codec; mp_msg(MSGT_CPLAYER, MSGL_INFO, " (%s)", codec ? codec : ""); if (t->is_external) mp_msg(MSGT_CPLAYER, MSGL_INFO, " (external)"); @@ -482,13 +480,8 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask) if (mask & INITIALIZED_SUB) { mpctx->initialized_flags &= ~INITIALIZED_SUB; - struct track *track = mpctx->current_track[STREAM_SUB]; - // One of these was active; they can't be both active. - assert(!(mpctx->sh_sub && track && track->sh_sub)); if (mpctx->sh_sub) sub_switchoff(mpctx->sh_sub, mpctx->osd); - if (track && track->sh_sub) - sub_switchoff(track->sh_sub, mpctx->osd); cleanup_demux_stream(mpctx, STREAM_SUB); reset_subtitles(mpctx); } @@ -1039,11 +1032,19 @@ static void add_dvd_tracks(struct MPContext *mpctx) #endif } +#ifdef CONFIG_ASS +static int free_ass_track(void *ptr) +{ + struct ass_track *track = *(struct ass_track **)ptr; + ass_free_track(track); + return 1; +} +#endif + struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename, float fps, int noerr) { struct MPOpts *opts = &mpctx->opts; - struct sh_sub *sh = NULL; struct ass_track *asst = NULL; const char *codec = NULL; @@ -1067,33 +1068,37 @@ struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename, } talloc_free(subd); } - if (asst) - sh = sd_ass_create_from_track(asst, codec, opts); + if (asst) { + struct demuxer *d = new_sub_pseudo_demuxer(opts); + assert(d->num_streams == 1); + struct sh_stream *s = d->streams[0]; + assert(s->type == STREAM_SUB); + + s->sub->track = asst; + s->codec = codec; + + struct ass_track **pptr = talloc(s, struct ass_track*); + *pptr = asst; + talloc_set_destructor(pptr, free_ass_track); + + struct track *t = add_stream_track(mpctx, s, false); + t->is_external = true; + t->preloaded = true; + t->title = talloc_strdup(t, filename); + t->external_filename = talloc_strdup(t, filename); + MP_TARRAY_APPEND(NULL, mpctx->sources, mpctx->num_sources, d); + return t; + } #endif - if (!sh) { - // Used with image subtitles. - struct track *ext = open_external_file(mpctx, filename, NULL, 0, - STREAM_SUB); - if (ext) - return ext; - mp_tmsg(MSGT_CPLAYER, noerr ? MSGL_WARN : MSGL_ERR, - "Cannot load subtitles: %s\n", filename); - return NULL; - } + // Used with libavformat subtitles. + struct track *ext = open_external_file(mpctx, filename, NULL, 0, STREAM_SUB); + if (ext) + return ext; - struct track *track = talloc_ptrtype(NULL, track); - *track = (struct track) { - .type = STREAM_SUB, - .title = talloc_strdup(track, filename), - .user_tid = find_new_tid(mpctx, STREAM_SUB), - .demuxer_id = -1, - .is_external = true, - .sh_sub = talloc_steal(track, sh), - .external_filename = talloc_strdup(track, filename), - }; - MP_TARRAY_APPEND(mpctx, mpctx->tracks, mpctx->num_tracks, track); - return track; + mp_tmsg(MSGT_CPLAYER, noerr ? MSGL_WARN : MSGL_ERR, + "Cannot load subtitles: %s\n", filename); + return NULL; } int mp_get_cache_percent(struct MPContext *mpctx) @@ -1845,7 +1850,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; - if (sh_sub && sh_sub->active) { + if (sh_sub && sh_sub->active && !track->preloaded) { struct demux_stream *d_sub = sh_sub->ds; const char *type = sh_sub->gsh->codec; bool non_interleaved = is_non_interleaved(mpctx, track); @@ -2017,9 +2022,7 @@ static void reinit_subs(struct MPContext *mpctx) 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, osd); - } else if (track->stream) { + if (track->stream) { if (track->demuxer && track->demuxer->stream) { set_dvdsub_fake_extradata(mpctx->sh_sub, track->demuxer->stream, osd->sub_video_w, osd->sub_video_h); diff --git a/demux/demux.c b/demux/demux.c index 3e27b43f93..12e0d9083a 100644 --- a/demux/demux.c +++ b/demux/demux.c @@ -66,6 +66,7 @@ extern const demuxer_desc_t demuxer_desc_mpeg_es; extern const demuxer_desc_t demuxer_desc_mpeg4_es; extern const demuxer_desc_t demuxer_desc_h264_es; extern const demuxer_desc_t demuxer_desc_mpeg_ts; +extern const demuxer_desc_t demuxer_desc_sub; /* Please do not add any new demuxers here. If you want to implement a new * demuxer, add it to libavformat, except for wrappers around external @@ -95,6 +96,8 @@ const demuxer_desc_t *const demuxer_list[] = { &demuxer_desc_mpeg_ts, // auto-probe last, because it checks file-extensions only &demuxer_desc_mf, + // no auto-probe + &demuxer_desc_sub, /* Please do not add any new demuxers here. If you want to implement a new * demuxer, add it to libavformat, except for wrappers around external * libraries and demuxers requiring binary support. */ @@ -217,8 +220,8 @@ static const demuxer_desc_t *get_demuxer_desc_from_type(int file_format) } -demuxer_t *new_demuxer(struct MPOpts *opts, stream_t *stream, int type, - int a_id, int v_id, int s_id, char *filename) +static demuxer_t *new_demuxer(struct MPOpts *opts, stream_t *stream, int type, + int a_id, int v_id, int s_id, char *filename) { struct demuxer *d = talloc_zero(NULL, struct demuxer); d->stream = stream; @@ -248,6 +251,18 @@ demuxer_t *new_demuxer(struct MPOpts *opts, stream_t *stream, int type, return d; } +// for demux_sub.c +demuxer_t *new_sub_pseudo_demuxer(struct MPOpts *opts) +{ + struct stream *s = open_stream("null://", NULL, NULL); + assert(s); + struct demuxer *d = new_demuxer(opts, s, DEMUXER_TYPE_SUB, + -1, -1, -1, NULL); + new_sh_stream(d, STREAM_SUB); + talloc_steal(d, s); + return d; +} + static struct sh_stream *new_sh_stream_id(demuxer_t *demuxer, enum stream_type type, int stream_index, diff --git a/demux/demux.h b/demux/demux.h index 886252fa85..e58c56141f 100644 --- a/demux/demux.h +++ b/demux/demux.h @@ -77,6 +77,7 @@ enum demuxer_type { DEMUXER_TYPE_END, DEMUXER_TYPE_PLAYLIST, + DEMUXER_TYPE_SUB, }; enum timestamp_type { @@ -304,9 +305,9 @@ static inline void *realloc_struct(void *ptr, size_t nmemb, size_t size) return realloc(ptr, nmemb * size); } -struct demuxer *new_demuxer(struct MPOpts *opts, struct stream *stream, - int type, int a_id, int v_id, int s_id, - char *filename); +demuxer_t *new_sub_pseudo_demuxer(struct MPOpts *opts); + + void free_demuxer(struct demuxer *demuxer); void demuxer_add_packet(demuxer_t *demuxer, struct sh_stream *stream, diff --git a/demux/demux_sub.c b/demux/demux_sub.c new file mode 100644 index 0000000000..ab99091215 --- /dev/null +++ b/demux/demux_sub.c @@ -0,0 +1,38 @@ +/* + * 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 . + */ + +// Note: not a real demuxer. The frontend has its own code to open subtitle +// code, and then creates a new dummy demuxer with new_sub_demuxer(). +// But eventually, all subtitles should be opened this way, and this +// file can be removed. + +#include "demux.h" + +static int dummy_fill_buffer(struct demuxer *demuxer, struct demux_stream *ds) +{ + return 0; +} + +const struct demuxer_desc demuxer_desc_sub = { + .info = "External subtitles pseudo demuxer", + .name = "sub", + .shortdesc = "sub", + .author = "", + .comment = "", + .type = DEMUXER_TYPE_SUB, + .fill_buffer = dummy_fill_buffer, +}; diff --git a/demux/stheader.h b/demux/stheader.h index 433dc7ef71..09e9d8682b 100644 --- a/demux/stheader.h +++ b/demux/stheader.h @@ -164,6 +164,7 @@ typedef struct sh_sub { bool active; // after track switch decoder may stay initialized, not active unsigned char *extradata; // extra header data passed from demuxer int extradata_len; + struct ass_track *track; // loaded by libass const struct sd_functions *sd_driver; } sh_sub_t; diff --git a/sub/dec_sub.h b/sub/dec_sub.h index 263df19bc0..4eb833c52b 100644 --- a/sub/dec_sub.h +++ b/sub/dec_sub.h @@ -21,9 +21,6 @@ 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, - const char *codec, struct MPOpts *opts); - #ifdef CONFIG_ASS struct ass_track *sub_get_ass_track(struct osd_state *osd); #endif diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 02d0ed15bf..b107e0438f 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -79,7 +79,9 @@ static int init(struct sh_sub *sh, struct osd_state *osd) } else { ctx = talloc_zero(NULL, struct sd_ass_priv); sh->context = ctx; - if (ass) { + if (sh->track) { + ctx->ass_track = sh->track; + } else if (ass) { ctx->ass_track = ass_new_track(osd->ass_library); if (sh->extradata) ass_process_codec_private(ctx->ass_track, sh->extradata, @@ -277,7 +279,8 @@ static void uninit(struct sh_sub *sh) { struct sd_ass_priv *ctx = sh->context; - ass_free_track(ctx->ass_track); + if (sh->track != ctx->ass_track) + ass_free_track(ctx->ass_track); talloc_free(ctx); } @@ -293,31 +296,6 @@ const struct sd_functions sd_ass = { .uninit = uninit, }; -static int sd_ass_track_destructor(void *ptr) -{ - uninit(ptr); - return 1; -} - -struct sh_sub *sd_ass_create_from_track(struct ass_track *track, - 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 = codec, - }), - .context = talloc_struct(sh, struct sd_ass_priv, { - .ass_track = track, - .vsfilter_aspect = is_ass_sub(codec), - }), - .initialized = true, - }; - return sh; -} - struct ass_track *sub_get_ass_track(struct osd_state *osd) { struct sh_sub *sh = osd ? osd->sh_sub : NULL; From 02ce316ade9ba932ad405383278d6b01c54e5fc4 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 1 Jun 2013 19:44:12 +0200 Subject: [PATCH 10/28] sub: refactor Make the sub decoder stuff independent from sh_sub (except for initialization of course). Sub decoders now access a struct sd only, instead of getting access to sh_sub. The glue code in dec_sub.c is similarily independent from osd. Some simplifications are made. For example, the switch_id stuff is unneeded: the frontend code just has to make sure to call osd_changed() any time subtitles are switched. This is also preparation for introducing subtitle converters. It's much cleaner to completely separate demuxer header/renderer glue/decoders for this purpose, especially since sub converters might completely change how demuxer headers have to be interpreted. Also pass data as demux_packets. Currently, this doesn't help much, but libavcodec converters might need scary stuff like packet side data, so it's perhaps better to go with passing packets. --- core/command.c | 4 +- core/mplayer.c | 80 ++++++++++++++--------- demux/stheader.h | 3 +- sub/dec_sub.c | 165 +++++++++++++++++++++++++++++------------------ sub/dec_sub.h | 36 ++++++++--- sub/sd.h | 40 +++++++++--- sub/sd_ass.c | 78 +++++++++++----------- sub/sd_lavc.c | 37 +++++------ sub/sd_spu.c | 36 +++++------ sub/sub.c | 4 +- sub/sub.h | 9 +-- 11 files changed, 287 insertions(+), 205 deletions(-) diff --git a/core/command.c b/core/command.c index 7971180553..416fbcd06a 100644 --- a/core/command.c +++ b/core/command.c @@ -1951,9 +1951,9 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) case MP_CMD_SUB_STEP: #ifdef CONFIG_ASS - if (sh_video) { + if (mpctx->osd->dec_sub) { int movement = cmd->args[0].v.i; - struct ass_track *ass_track = sub_get_ass_track(mpctx->osd); + struct ass_track *ass_track = sub_get_ass_track(mpctx->osd->dec_sub); if (ass_track) { set_osd_tmsg(mpctx, OSD_MSG_SUB_DELAY, osdl, osd_duration, "Sub delay: %d ms", ROUND(opts->sub_delay * 1000)); diff --git a/core/mplayer.c b/core/mplayer.c index a19788df0b..92c0f717af 100644 --- a/core/mplayer.c +++ b/core/mplayer.c @@ -457,8 +457,10 @@ static void uninit_subs(struct demuxer *demuxer) { for (int i = 0; i < MAX_S_STREAMS; i++) { struct sh_sub *sh = demuxer->s_streams[i]; - if (sh && sh->initialized) - sub_uninit(sh); + if (sh) { + sub_destroy(sh->dec_sub); + sh->dec_sub = NULL; + } } } @@ -481,8 +483,9 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask) if (mask & INITIALIZED_SUB) { mpctx->initialized_flags &= ~INITIALIZED_SUB; if (mpctx->sh_sub) - sub_switchoff(mpctx->sh_sub, mpctx->osd); + sub_reset(mpctx->sh_sub->dec_sub); cleanup_demux_stream(mpctx, STREAM_SUB); + mpctx->osd->dec_sub = NULL; reset_subtitles(mpctx); } @@ -1077,7 +1080,7 @@ struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename, s->sub->track = asst; s->codec = codec; - struct ass_track **pptr = talloc(s, struct ass_track*); + struct ass_track **pptr = talloc(d, struct ass_track*); *pptr = asst; talloc_set_destructor(pptr, free_ass_track); @@ -1830,18 +1833,21 @@ static bool is_non_interleaved(struct MPContext *mpctx, struct track *track) static void reset_subtitles(struct MPContext *mpctx) { if (mpctx->sh_sub) - sub_reset(mpctx->sh_sub, mpctx->osd); + sub_reset(mpctx->sh_sub->dec_sub); set_osd_subtitle(mpctx, NULL); + osd_changed(mpctx->osd, OSDTYPE_SUB); } static void update_subtitles(struct MPContext *mpctx, double refpts_tl) { struct MPOpts *opts = &mpctx->opts; - struct sh_sub *sh_sub = mpctx->sh_sub; + if (!(mpctx->initialized_flags & INITIALIZED_SUB)) + return; struct track *track = mpctx->current_track[STREAM_SUB]; - if (!track) - return; + struct sh_sub *sh_sub = mpctx->sh_sub; + assert(track && sh_sub); + struct dec_sub *dec_sub = sh_sub->dec_sub; double video_offset = track->under_timeline ? mpctx->video_offset : 0; @@ -1850,7 +1856,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; - if (sh_sub && sh_sub->active && !track->preloaded) { + if (!track->preloaded) { struct demux_stream *d_sub = sh_sub->ds; const char *type = sh_sub->gsh->codec; bool non_interleaved = is_non_interleaved(mpctx, track); @@ -1880,7 +1886,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 (!sub_accept_packets_in_advance(sh_sub)) + if (!sub_accept_packets_in_advance(dec_sub)) break; // Try to avoid demuxing whole file at once if (non_interleaved && subpts_s > curpts_s + 1) @@ -1898,12 +1904,18 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl) len = FFMIN(len - 2, AV_RB16(packet)); packet += 2; } - sub_decode(sh_sub, mpctx->osd, packet, len, subpts_s, duration); + struct demux_packet pkt = { + .buffer = packet, + .len = len, + .pts = subpts_s, + .duration = duration, + }; + sub_decode(dec_sub, &pkt); } } if (!mpctx->osd->render_bitmap_subs) - set_osd_subtitle(mpctx, sub_get_text(mpctx->osd, curpts_s)); + set_osd_subtitle(mpctx, sub_get_text(dec_sub, curpts_s)); } static int check_framedrop(struct MPContext *mpctx, double frame_time) @@ -1946,7 +1958,7 @@ static double timing_sleep(struct MPContext *mpctx, double time_frame) return time_frame; } -static void set_dvdsub_fake_extradata(struct sh_sub *sh_sub, struct stream *st, +static void set_dvdsub_fake_extradata(struct dec_sub *dec_sub, struct stream *st, int width, int height) { #ifdef CONFIG_DVDREAD @@ -1981,9 +1993,7 @@ static void set_dvdsub_fake_extradata(struct sh_sub *sh_sub, struct stream *st, } s = talloc_asprintf_append(s, "\n"); - free(sh_sub->extradata); - sh_sub->extradata = strdup(s); - sh_sub->extradata_len = strlen(s); + sub_set_extradata(dec_sub, s, strlen(s)); talloc_free(s); #endif } @@ -1992,15 +2002,16 @@ 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)); init_demux_stream(mpctx, STREAM_SUB); - - if (!track) + if (!mpctx->sh_sub) return; + if (!mpctx->sh_sub->dec_sub) + mpctx->sh_sub->dec_sub = sub_create(opts); + if (track->demuxer && !track->stream) { // Lazily added DVD track - we must not miss the first subtitle packet, // which makes the demuxer create the sh_stream, and contains the first @@ -2016,25 +2027,32 @@ static void reinit_subs(struct MPContext *mpctx) return; } + assert(track->demuxer && track->stream); 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; + struct sh_sub *sh_sub = mpctx->sh_sub; + struct dec_sub *dec_sub = sh_sub->dec_sub; + assert(dec_sub); - if (track->stream) { - 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); + if (!sub_is_initialized(dec_sub)) { + int w = mpctx->sh_video ? mpctx->sh_video->disp_w : 0; + int h = mpctx->sh_video ? mpctx->sh_video->disp_h : 0; + + set_dvdsub_fake_extradata(dec_sub, track->demuxer->stream, w, h); + sub_set_video_res(dec_sub, w, h); + sub_set_ass_renderer(dec_sub, mpctx->osd->ass_library, + mpctx->osd->ass_renderer); + sub_init_from_sh(dec_sub, sh_sub); } + mpctx->osd->dec_sub = dec_sub; + // 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; + mpctx->osd->render_bitmap_subs = + opts->ass_enabled || !sub_has_get_text(dec_sub); + + reset_subtitles(mpctx); } static char *track_layout_hash(struct MPContext *mpctx) diff --git a/demux/stheader.h b/demux/stheader.h index 09e9d8682b..8220d65a61 100644 --- a/demux/stheader.h +++ b/demux/stheader.h @@ -161,11 +161,10 @@ typedef struct sh_video { typedef struct sh_sub { SH_COMMON - bool active; // after track switch decoder may stay initialized, not active unsigned char *extradata; // extra header data passed from demuxer int extradata_len; struct ass_track *track; // loaded by libass - const struct sd_functions *sd_driver; + struct dec_sub *dec_sub; // decoder context } sh_sub_t; // demuxer.c: diff --git a/sub/dec_sub.c b/sub/dec_sub.c index 2cc02efb79..67828921b4 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -40,95 +40,136 @@ static const struct sd_functions *sd_list[] = { NULL }; -void sub_init(struct sh_sub *sh, struct osd_state *osd) +struct dec_sub { + struct MPOpts *opts; + struct sd init_sd; + + struct sd *sd; +}; + +struct dec_sub *sub_create(struct MPOpts *opts) { - sh->sd_driver = NULL; + struct dec_sub *sub = talloc_zero(NULL, struct dec_sub); + sub->opts = opts; + return sub; +} + +void sub_destroy(struct dec_sub *sub) +{ + if (!sub) + return; + if (sub->sd && sub->sd->driver->uninit) + sub->sd->driver->uninit(sub->sd); + talloc_free(sub->sd); + talloc_free(sub); +} + +bool sub_is_initialized(struct dec_sub *sub) +{ + return !!sub->sd; +} + +struct sd *sub_get_sd(struct dec_sub *sub) +{ + return sub->sd; +} + +void sub_set_video_res(struct dec_sub *sub, int w, int h) +{ + sub->init_sd.sub_video_w = w; + sub->init_sd.sub_video_h = h; +} + +void sub_set_extradata(struct dec_sub *sub, void *data, int data_len) +{ + sub->init_sd.extradata = data_len ? talloc_memdup(sub, data, data_len) : NULL; + sub->init_sd.extradata_len = data_len; +} + +void sub_set_ass_renderer(struct dec_sub *sub, struct ass_library *ass_library, + struct ass_renderer *ass_renderer) +{ + sub->init_sd.ass_library = ass_library; + sub->init_sd.ass_renderer = ass_renderer; +} + +static int sub_init_decoder(struct dec_sub *sub, struct sd *sd) +{ + 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]; + if (sd_list[n]->supports_format(sd->codec)) { + sd->driver = sd_list[n]; break; } } - if (sh->sd_driver) { - if (sh->sd_driver->init(sh, osd) < 0) - return; - osd->sh_sub = sh; - osd->switch_sub_id++; - sh->initialized = true; - sh->active = true; + if (!sd->driver) + return -1; + + if (sd->driver->init(sd) < 0) + return -1; + + return 0; +} + +void sub_init_from_sh(struct dec_sub *sub, struct sh_sub *sh) +{ + assert(!sub->sd); + if (sh->extradata && !sub->init_sd.extradata) + sub_set_extradata(sub, sh->extradata, sh->extradata_len); + struct sd *sd = talloc(NULL, struct sd); + *sd = sub->init_sd; + sd->opts = sub->opts; + sd->codec = sh->gsh->codec; + sd->ass_track = sh->track; + if (sub_init_decoder(sub, sd) < 0) { + talloc_free(sd); + sd = NULL; } + sub->sd = sd; } -bool sub_accept_packets_in_advance(struct sh_sub *sh) +bool sub_accept_packets_in_advance(struct dec_sub *sub) { - return sh->active && sh->sd_driver->accept_packets_in_advance; + return sub->sd && sub->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) +void sub_decode(struct dec_sub *sub, struct demux_packet *packet) { - if (sh->active && sh->sd_driver->decode) - sh->sd_driver->decode(sh, osd, data, data_len, pts, duration); + if (sub->sd) + sub->sd->driver->decode(sub->sd, packet); } -void sub_get_bitmaps(struct osd_state *osd, struct mp_osd_res dim, double pts, +void sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, double pts, struct sub_bitmaps *res) { - struct MPOpts *opts = osd->opts; + struct MPOpts *opts = sub->opts; *res = (struct sub_bitmaps) {0}; - if (!opts->sub_visibility || !osd->sh_sub || !osd->sh_sub->active) { - /* Change ID in case we just switched from visible subtitles - * to current state. Hopefully, unnecessarily claiming that - * things may have changed is harmless for empty contents. - * Increase osd-> values ahead so that _next_ returned id - * is also guaranteed to differ from this one. - */ - osd->switch_sub_id++; - } else { - if (osd->sh_sub->sd_driver->get_bitmaps) - osd->sh_sub->sd_driver->get_bitmaps(osd->sh_sub, osd, dim, pts, res); + if (sub->sd && opts->sub_visibility) { + if (sub->sd->driver->get_bitmaps) + sub->sd->driver->get_bitmaps(sub->sd, dim, pts, res); } - - res->bitmap_id += osd->switch_sub_id; - res->bitmap_pos_id += osd->switch_sub_id; - osd->switch_sub_id = 0; } -char *sub_get_text(struct osd_state *osd, double pts) +bool sub_has_get_text(struct dec_sub *sub) { - struct MPOpts *opts = osd->opts; + return sub->sd && sub->sd->driver->get_text; +} + +char *sub_get_text(struct dec_sub *sub, double pts) +{ + struct MPOpts *opts = sub->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); + if (sub->sd && opts->sub_visibility) { + if (sub->sd->driver->get_text) + text = sub->sd->driver->get_text(sub->sd, pts); } return text; } -void sub_reset(struct sh_sub *sh, struct osd_state *osd) +void sub_reset(struct dec_sub *sub) { - if (sh->active && sh->sd_driver->reset) - sh->sd_driver->reset(sh, osd); -} - -void sub_switchoff(struct sh_sub *sh, struct osd_state *osd) -{ - if (sh->active && sh->sd_driver->switch_off) { - assert(osd->sh_sub == sh); - sh->sd_driver->switch_off(sh, osd); - osd->sh_sub = NULL; - } - sh->active = false; -} - -void sub_uninit(struct sh_sub *sh) -{ - assert (!sh->active); - if (sh->initialized && sh->sd_driver->uninit) - sh->sd_driver->uninit(sh); - sh->initialized = false; + if (sub->sd && sub->sd->driver->reset) + sub->sd->driver->reset(sub->sd); } diff --git a/sub/dec_sub.h b/sub/dec_sub.h index 4eb833c52b..39632d21a9 100644 --- a/sub/dec_sub.h +++ b/sub/dec_sub.h @@ -9,20 +9,36 @@ struct sh_sub; struct ass_track; struct MPOpts; +struct demux_packet; +struct ass_library; +struct ass_renderer; -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 dec_sub; +struct sd; + +struct dec_sub *sub_create(struct MPOpts *opts); +void sub_destroy(struct dec_sub *sub); + +void sub_set_video_res(struct dec_sub *sub, int w, int h); +void sub_set_extradata(struct dec_sub *sub, void *data, int data_len); +void sub_set_ass_renderer(struct dec_sub *sub, struct ass_library *ass_library, + struct ass_renderer *ass_renderer); +void sub_init_from_sh(struct dec_sub *sub, struct sh_sub *sh); + +bool sub_is_initialized(struct dec_sub *sub); + +bool sub_accept_packets_in_advance(struct dec_sub *sub); +void sub_decode(struct dec_sub *sub, struct demux_packet *packet); +void sub_get_bitmaps(struct dec_sub *sub, 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); +bool sub_has_get_text(struct dec_sub *sub); +char *sub_get_text(struct dec_sub *sub, double pts); +void sub_reset(struct dec_sub *sub); + +struct sd *sub_get_sd(struct dec_sub *sub); #ifdef CONFIG_ASS -struct ass_track *sub_get_ass_track(struct osd_state *osd); +struct ass_track *sub_get_ass_track(struct dec_sub *sub); #endif #endif diff --git a/sub/sd.h b/sub/sd.h index 123a9bc45d..42f7b8a445 100644 --- a/sub/sd.h +++ b/sub/sd.h @@ -2,20 +2,42 @@ #define MPLAYER_SD_H #include "dec_sub.h" +#include "demux/demux_packet.h" + +struct sd { + struct MPOpts *opts; + + const struct sd_functions *driver; + void *priv; + + const char *codec; + + // Extra header data passed from demuxer + char *extradata; + int extradata_len; + + // 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; + + // Make sd_ass use an existing track + struct ass_track *ass_track; + + // Shared renderer for ASS - done to avoid reloading embedded fonts. + struct ass_library *ass_library; + struct ass_renderer *ass_renderer; +}; struct sd_functions { 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, + int (*init)(struct sd *sd); + void (*decode)(struct sd *sd, struct demux_packet *packet); + void (*get_bitmaps)(struct sd *sd, 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); + char *(*get_text)(struct sd *sd, double pts); + void (*reset)(struct sd *sd); + void (*uninit)(struct sd *sd); }; #endif diff --git a/sub/sd_ass.c b/sub/sd_ass.c index b107e0438f..2ebd2164be 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -69,39 +69,39 @@ static void free_last_event(ASS_Track *track) track->n_events--; } -static int init(struct sh_sub *sh, struct osd_state *osd) +static int init(struct sd *sd) { - struct sd_ass_priv *ctx; - bool ass = is_ass_sub(sh->gsh->codec); + if (!sd->ass_library || !sd->ass_renderer) + return -1; - if (sh->initialized) { - ctx = sh->context; - } else { - ctx = talloc_zero(NULL, struct sd_ass_priv); - sh->context = ctx; - if (sh->track) { - ctx->ass_track = sh->track; - } else if (ass) { - ctx->ass_track = ass_new_track(osd->ass_library); - if (sh->extradata) - ass_process_codec_private(ctx->ass_track, sh->extradata, - sh->extradata_len); - } else - ctx->ass_track = mp_ass_default_track(osd->ass_library, sh->opts); - } + bool ass = is_ass_sub(sd->codec); + struct sd_ass_priv *ctx = talloc_zero(NULL, struct sd_ass_priv); + sd->priv = ctx; + if (sd->ass_track) { + ctx->ass_track = sd->ass_track; + } else if (ass) { + ctx->ass_track = ass_new_track(sd->ass_library); + if (sd->extradata) + ass_process_codec_private(ctx->ass_track, sd->extradata, + sd->extradata_len); + } else + ctx->ass_track = mp_ass_default_track(sd->ass_library, sd->opts); ctx->vsfilter_aspect = ass; return 0; } -static void decode(struct sh_sub *sh, struct osd_state *osd, void *data, - int data_len, double pts, double duration) +static void decode(struct sd *sd, struct demux_packet *packet) { + void *data = packet->buffer; + int data_len = packet->len; + double pts = packet->pts; + double duration = packet->duration; unsigned char *text = data; - struct sd_ass_priv *ctx = sh->context; + struct sd_ass_priv *ctx = sd->priv; ASS_Track *track = ctx->ass_track; - if (is_ass_sub(sh->gsh->codec)) { + if (is_ass_sub(sd->codec)) { if (bstr_startswith0((bstr){data, data_len}, "Dialogue: ")) { // broken ffmpeg ASS packet format ctx->flush_on_seek = true; @@ -158,14 +158,13 @@ static void decode(struct sh_sub *sh, struct osd_state *osd, void *data, event->Text = strdup(buf); } -static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd, - struct mp_osd_res dim, double pts, +static void get_bitmaps(struct sd *sd, struct mp_osd_res dim, double pts, struct sub_bitmaps *res) { - struct sd_ass_priv *ctx = sh->context; - struct MPOpts *opts = osd->opts; + struct sd_ass_priv *ctx = sd->priv; + struct MPOpts *opts = sd->opts; - if (pts == MP_NOPTS_VALUE) + if (pts == MP_NOPTS_VALUE || !sd->ass_renderer) return; double scale = dim.display_par; @@ -173,7 +172,7 @@ static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd, ? opts->ass_vsfilter_aspect_compat : 1; if (ctx->vsfilter_aspect && use_vs_aspect) scale = scale * dim.video_par; - ASS_Renderer *renderer = osd->ass_renderer; + ASS_Renderer *renderer = sd->ass_renderer; mp_ass_configure(renderer, opts, &dim); ass_set_aspect_ratio(renderer, scale, 1); mp_ass_render_frame(renderer, ctx->ass_track, pts * 1000 + .5, @@ -234,9 +233,9 @@ static void ass_to_plaintext(struct buf *b, const char *in) } } -static char *get_text(struct sh_sub *sh, struct osd_state *osd, double pts) +static char *get_text(struct sd *sd, double pts) { - struct sd_ass_priv *ctx = sh->context; + struct sd_ass_priv *ctx = sd->priv; ASS_Track *track = ctx->ass_track; if (pts == MP_NOPTS_VALUE) @@ -264,9 +263,9 @@ static char *get_text(struct sh_sub *sh, struct osd_state *osd, double pts) return ctx->last_text; } -static void reset(struct sh_sub *sh, struct osd_state *osd) +static void reset(struct sd *sd) { - struct sd_ass_priv *ctx = sh->context; + struct sd_ass_priv *ctx = sd->priv; if (ctx->incomplete_event) free_last_event(ctx->ass_track); ctx->incomplete_event = false; @@ -275,11 +274,11 @@ static void reset(struct sh_sub *sh, struct osd_state *osd) ctx->flush_on_seek = false; } -static void uninit(struct sh_sub *sh) +static void uninit(struct sd *sd) { - struct sd_ass_priv *ctx = sh->context; + struct sd_ass_priv *ctx = sd->priv; - if (sh->track != ctx->ass_track) + if (sd->ass_track != ctx->ass_track) ass_free_track(ctx->ass_track); talloc_free(ctx); } @@ -292,15 +291,14 @@ const struct sd_functions sd_ass = { .get_bitmaps = get_bitmaps, .get_text = get_text, .reset = reset, - .switch_off = reset, .uninit = uninit, }; -struct ass_track *sub_get_ass_track(struct osd_state *osd) +struct ass_track *sub_get_ass_track(struct dec_sub *sub) { - struct sh_sub *sh = osd ? osd->sh_sub : NULL; - if (sh && sh->sd_driver == &sd_ass && sh->context) { - struct sd_ass_priv *ctx = sh->context; + struct sd *sd = sub_get_sd(sub); + if (sd && sd->driver == &sd_ass && sd->priv) { + struct sd_ass_priv *ctx = sd->priv; return ctx->ass_track; } return NULL; diff --git a/sub/sd_lavc.c b/sub/sd_lavc.c index 4c7dfd12a5..9f8db2d877 100644 --- a/sub/sd_lavc.c +++ b/sub/sd_lavc.c @@ -81,12 +81,10 @@ static void guess_resolution(enum AVCodecID type, int *w, int *h) } } -static int init(struct sh_sub *sh, struct osd_state *osd) +static int init(struct sd *sd) { - if (sh->initialized) - return 0; struct sd_lavc_priv *priv = talloc_zero(NULL, struct sd_lavc_priv); - enum AVCodecID cid = mp_codec_to_av_codec_id(sh->gsh->codec); + enum AVCodecID cid = mp_codec_to_av_codec_id(sd->codec); AVCodecContext *ctx = NULL; AVCodec *sub_codec = avcodec_find_decoder(cid); if (!sub_codec) @@ -94,12 +92,12 @@ static int init(struct sh_sub *sh, struct osd_state *osd) ctx = avcodec_alloc_context3(sub_codec); if (!ctx) goto error; - ctx->extradata_size = sh->extradata_len; - ctx->extradata = sh->extradata; + ctx->extradata_size = sd->extradata_len; + ctx->extradata = sd->extradata; if (avcodec_open2(ctx, sub_codec, NULL) < 0) goto error; priv->avctx = ctx; - sh->context = priv; + sd->priv = priv; return 0; error: @@ -126,18 +124,19 @@ static void clear(struct sd_lavc_priv *priv) priv->have_sub = false; } -static void decode(struct sh_sub *sh, struct osd_state *osd, void *data, - int data_len, double pts, double duration) +static void decode(struct sd *sd, struct demux_packet *packet) { - struct sd_lavc_priv *priv = sh->context; + struct sd_lavc_priv *priv = sd->priv; AVCodecContext *ctx = priv->avctx; + double pts = packet->pts; + double duration = packet->duration; AVSubtitle sub; AVPacket pkt; clear(priv); av_init_packet(&pkt); - pkt.data = data; - pkt.size = data_len; + pkt.data = packet->buffer; + pkt.size = packet->len; pkt.pts = pts * 1000; if (duration >= 0) pkt.convergence_duration = duration * 1000; @@ -189,11 +188,10 @@ static void decode(struct sh_sub *sh, struct osd_state *osd, void *data, } } -static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd, - struct mp_osd_res d, double pts, +static void get_bitmaps(struct sd *sd, struct mp_osd_res d, double pts, struct sub_bitmaps *res) { - struct sd_lavc_priv *priv = sh->context; + struct sd_lavc_priv *priv = sd->priv; if (priv->pts != MP_NOPTS_VALUE && pts < priv->pts) return; @@ -225,9 +223,9 @@ static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd, res->scaled = xscale != 1 || yscale != 1; } -static void reset(struct sh_sub *sh, struct osd_state *osd) +static void reset(struct sd *sd) { - struct sd_lavc_priv *priv = sh->context; + struct sd_lavc_priv *priv = sd->priv; if (priv->pts == MP_NOPTS_VALUE) clear(priv); @@ -235,9 +233,9 @@ static void reset(struct sh_sub *sh, struct osd_state *osd) avcodec_flush_buffers(priv->avctx); } -static void uninit(struct sh_sub *sh) +static void uninit(struct sd *sd) { - struct sd_lavc_priv *priv = sh->context; + struct sd_lavc_priv *priv = sd->priv; clear(priv); avcodec_close(priv->avctx); @@ -251,6 +249,5 @@ const struct sd_functions sd_lavc = { .decode = decode, .get_bitmaps = get_bitmaps, .reset = reset, - .switch_off = reset, .uninit = uninit, }; diff --git a/sub/sd_spu.c b/sub/sd_spu.c index 8b87b7a0dc..d2dd5f56e0 100644 --- a/sub/sd_spu.c +++ b/sub/sd_spu.c @@ -40,37 +40,34 @@ static bool supports_format(const char *format) return is_dvd_sub(format); } -static int init(struct sh_sub *sh, struct osd_state *osd) +static int init(struct sd *sd) { - if (sh->initialized) - return 0; - void *spudec = spudec_new_scaled(osd->sub_video_w, osd->sub_video_h, - sh->extradata, sh->extradata_len); + void *spudec = spudec_new_scaled(sd->sub_video_w, sd->sub_video_h, + sd->extradata, sd->extradata_len); if (!spudec) return -1; struct sd_spu_priv *priv = talloc_zero(NULL, struct sd_spu_priv); priv->spudec = spudec; - sh->context = priv; + sd->priv = priv; return 0; } -static void decode(struct sh_sub *sh, struct osd_state *osd, void *data, - int data_len, double pts, double duration) +static void decode(struct sd *sd, struct demux_packet *packet) { - struct sd_spu_priv *priv = sh->context; + struct sd_spu_priv *priv = sd->priv; - if (pts < 0 || data_len == 0) + if (packet->pts < 0 || packet->len == 0) return; - spudec_assemble(priv->spudec, data, data_len, pts * 90000); + spudec_assemble(priv->spudec, packet->buffer, packet->len, + packet->pts * 90000); } -static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd, - struct mp_osd_res d, double pts, +static void get_bitmaps(struct sd *sd, struct mp_osd_res d, double pts, struct sub_bitmaps *res) { - struct MPOpts *opts = sh->opts; - struct sd_spu_priv *priv = sh->context; + struct MPOpts *opts = sd->opts; + struct sd_spu_priv *priv = sd->priv; spudec_set_forced_subs_only(priv->spudec, opts->forced_subs_only); spudec_heartbeat(priv->spudec, pts * 90000); @@ -79,16 +76,16 @@ static void get_bitmaps(struct sh_sub *sh, struct osd_state *osd, spudec_get_indexed(priv->spudec, &d, res); } -static void reset(struct sh_sub *sh, struct osd_state *osd) +static void reset(struct sd *sd) { - struct sd_spu_priv *priv = sh->context; + struct sd_spu_priv *priv = sd->priv; spudec_reset(priv->spudec); } -static void uninit(struct sh_sub *sh) +static void uninit(struct sd *sd) { - struct sd_spu_priv *priv = sh->context; + struct sd_spu_priv *priv = sd->priv; spudec_free(priv->spudec); talloc_free(priv); @@ -100,6 +97,5 @@ const struct sd_functions sd_spu = { .decode = decode, .get_bitmaps = get_bitmaps, .reset = reset, - .switch_off = reset, .uninit = uninit, }; diff --git a/sub/sub.c b/sub/sub.c index 9dc5722469..a0965dc1ec 100644 --- a/sub/sub.c +++ b/sub/sub.c @@ -159,11 +159,11 @@ static void render_object(struct osd_state *osd, struct osd_object *obj, obj->vo_res = res; if (obj->type == OSDTYPE_SUB) { - if (osd->render_bitmap_subs) { + if (osd->render_bitmap_subs && osd->dec_sub) { 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); + sub_get_bitmaps(osd->dec_sub, 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 fae7202ed5..a13d3ca6f8 100644 --- a/sub/sub.h +++ b/sub/sub.h @@ -120,7 +120,6 @@ struct osd_state { struct ass_library *ass_library; struct ass_renderer *ass_renderer; - struct sh_sub *sh_sub; double sub_offset; double vo_pts; @@ -138,15 +137,11 @@ struct osd_state { 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; + // OSDTYPE_SUB + struct dec_sub *dec_sub; 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; From 3000df35d31f09f13a7c662e2f96bcd7d0f6ac13 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 1 Jun 2013 19:44:55 +0200 Subject: [PATCH 11/28] sub: basic subtitle converters Add a basic infrastructure for subtitle converters. These converters work sort-of like decoders, except that they produce packets instead of subtitle bitmaps. They are put in front of actual decoders. Start with sd_movtext. 4 lines of code are blown up to a 55 lines file, but fortunately this is not going to be that bad for the following converters. --- Makefile | 1 + core/mplayer.c | 7 -- sub/dec_sub.c | 170 +++++++++++++++++++++++++++++++++++++++-------- sub/dec_sub.h | 2 +- sub/sd.h | 23 ++++++- sub/sd_ass.c | 5 +- sub/sd_movtext.c | 54 +++++++++++++++ 7 files changed, 220 insertions(+), 42 deletions(-) create mode 100644 sub/sd_movtext.c diff --git a/Makefile b/Makefile index 6e52253543..583eaf87ec 100644 --- a/Makefile +++ b/Makefile @@ -231,6 +231,7 @@ SOURCES = talloc.c \ sub/find_subfiles.c \ sub/img_convert.c \ sub/sd_lavc.c \ + sub/sd_movtext.c \ sub/sd_spu.c \ sub/spudec.c \ sub/sub.c \ diff --git a/core/mplayer.c b/core/mplayer.c index 92c0f717af..1fae2eece8 100644 --- a/core/mplayer.c +++ b/core/mplayer.c @@ -1858,7 +1858,6 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl) if (!track->preloaded) { struct demux_stream *d_sub = sh_sub->ds; - const char *type = sh_sub->gsh->codec; bool non_interleaved = is_non_interleaved(mpctx, track); while (1) { @@ -1898,12 +1897,6 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl) mp_dbg(MSGT_CPLAYER, MSGL_V, "Sub: c_pts=%5.3f s_pts=%5.3f " "duration=%5.3f len=%d\n", curpts_s, subpts_s, duration, len); - if (type && strcmp(type, "mov_text") == 0) { - if (len < 2) - continue; - len = FFMIN(len - 2, AV_RB16(packet)); - packet += 2; - } struct demux_packet pkt = { .buffer = packet, .len = len, diff --git a/sub/dec_sub.c b/sub/dec_sub.c index 67828921b4..6ef1e4a8cf 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -26,10 +26,12 @@ #include "sub/sub.h" #include "sub/dec_sub.h" #include "core/options.h" +#include "core/mp_msg.h" extern const struct sd_functions sd_ass; extern const struct sd_functions sd_lavc; extern const struct sd_functions sd_spu; +extern const struct sd_functions sd_movtext; static const struct sd_functions *sd_list[] = { #ifdef CONFIG_ASS @@ -37,14 +39,18 @@ static const struct sd_functions *sd_list[] = { #endif &sd_lavc, &sd_spu, + &sd_movtext, NULL }; +#define MAX_NUM_SD 2 + struct dec_sub { struct MPOpts *opts; struct sd init_sd; - struct sd *sd; + struct sd *sd[MAX_NUM_SD]; + int num_sd; }; struct dec_sub *sub_create(struct MPOpts *opts) @@ -54,24 +60,33 @@ struct dec_sub *sub_create(struct MPOpts *opts) return sub; } +static void sub_uninit(struct dec_sub *sub) +{ + sub_reset(sub); + for (int n = 0; n < sub->num_sd; n++) { + if (sub->sd[n]->driver->uninit) + sub->sd[n]->driver->uninit(sub->sd[n]); + talloc_free(sub->sd[n]); + } + sub->num_sd = 0; +} + void sub_destroy(struct dec_sub *sub) { if (!sub) return; - if (sub->sd && sub->sd->driver->uninit) - sub->sd->driver->uninit(sub->sd); - talloc_free(sub->sd); + sub_uninit(sub); talloc_free(sub); } bool sub_is_initialized(struct dec_sub *sub) { - return !!sub->sd; + return !!sub->num_sd; } -struct sd *sub_get_sd(struct dec_sub *sub) +struct sd *sub_get_last_sd(struct dec_sub *sub) { - return sub->sd; + return sub->num_sd ? sub->sd[sub->num_sd - 1] : NULL; } void sub_set_video_res(struct dec_sub *sub, int w, int h) @@ -114,62 +129,159 @@ static int sub_init_decoder(struct dec_sub *sub, struct sd *sd) void sub_init_from_sh(struct dec_sub *sub, struct sh_sub *sh) { - assert(!sub->sd); + assert(!sub->num_sd); + if (sh->extradata && !sub->init_sd.extradata) sub_set_extradata(sub, sh->extradata, sh->extradata_len); - struct sd *sd = talloc(NULL, struct sd); - *sd = sub->init_sd; - sd->opts = sub->opts; - sd->codec = sh->gsh->codec; - sd->ass_track = sh->track; - if (sub_init_decoder(sub, sd) < 0) { - talloc_free(sd); - sd = NULL; + struct sd init_sd = sub->init_sd; + init_sd.codec = sh->gsh->codec; + init_sd.ass_track = sh->track; + + while (sub->num_sd < MAX_NUM_SD) { + struct sd *sd = talloc(NULL, struct sd); + *sd = init_sd; + sd->opts = sub->opts; + if (sub_init_decoder(sub, sd) < 0) { + talloc_free(sd); + break; + } + sub->sd[sub->num_sd] = sd; + sub->num_sd++; + // Try adding new converters until a decoder is reached + if (sd->driver->get_bitmaps || sd->driver->get_text) + return; + init_sd = (struct sd) { + .codec = sd->output_codec, + .extradata = sd->output_extradata, + .extradata_len = sd->output_extradata_len, + .ass_library = sub->init_sd.ass_library, + .ass_renderer = sub->init_sd.ass_renderer, + }; } - sub->sd = sd; + + sub_uninit(sub); + mp_msg(MSGT_OSD, MSGL_ERR, "Could not find subtitle decoder for format '%s'.\n", + sh->gsh->codec ? sh->gsh->codec : ""); } bool sub_accept_packets_in_advance(struct dec_sub *sub) { - return sub->sd && sub->sd->driver->accept_packets_in_advance; + // Converters are assumed to always accept packets in advance + struct sd *sd = sub_get_last_sd(sub); + return sd && sd->driver->accept_packets_in_advance; +} + +static void decode_next(struct dec_sub *sub, int n, struct demux_packet *packet) +{ + struct sd *sd = sub->sd[n]; + sd->driver->decode(sd, packet); + if (n + 1 >= sub->num_sd || !sd->driver->get_converted) + return; + while (1) { + struct demux_packet *next = + sd->driver->get_converted ? sd->driver->get_converted(sd) : NULL; + if (!next) + break; + decode_next(sub, n + 1, next); + } } void sub_decode(struct dec_sub *sub, struct demux_packet *packet) { - if (sub->sd) - sub->sd->driver->decode(sub->sd, packet); + if (sub->num_sd > 0) + decode_next(sub, 0, packet); } void sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, double pts, struct sub_bitmaps *res) { struct MPOpts *opts = sub->opts; + struct sd *sd = sub_get_last_sd(sub); *res = (struct sub_bitmaps) {0}; - if (sub->sd && opts->sub_visibility) { - if (sub->sd->driver->get_bitmaps) - sub->sd->driver->get_bitmaps(sub->sd, dim, pts, res); + if (sd && opts->sub_visibility) { + if (sd->driver->get_bitmaps) + sd->driver->get_bitmaps(sd, dim, pts, res); } } bool sub_has_get_text(struct dec_sub *sub) { - return sub->sd && sub->sd->driver->get_text; + struct sd *sd = sub_get_last_sd(sub); + return sd && sd->driver->get_text; } char *sub_get_text(struct dec_sub *sub, double pts) { struct MPOpts *opts = sub->opts; + struct sd *sd = sub_get_last_sd(sub); char *text = NULL; - if (sub->sd && opts->sub_visibility) { - if (sub->sd->driver->get_text) - text = sub->sd->driver->get_text(sub->sd, pts); + if (sd && opts->sub_visibility) { + if (sd->driver->get_text) + text = sd->driver->get_text(sd, pts); } return text; } void sub_reset(struct dec_sub *sub) { - if (sub->sd && sub->sd->driver->reset) - sub->sd->driver->reset(sub->sd); + for (int n = 0; n < sub->num_sd; n++) { + if (sub->sd[n]->driver->reset) + sub->sd[n]->driver->reset(sub->sd[n]); + } +} + +#define MAX_PACKETS 10 +#define MAX_BYTES 10000 + +struct sd_conv_buffer { + struct demux_packet pkt[MAX_PACKETS]; + int num_pkt; + int read_pkt; + char buffer[MAX_BYTES]; + int cur_buffer; +}; + +void sd_conv_add_packet(struct sd *sd, void *data, int data_len, double pts, + double duration) +{ + if (!sd->sd_conv_buffer) + sd->sd_conv_buffer = talloc_zero(sd, struct sd_conv_buffer); + struct sd_conv_buffer *buf = sd->sd_conv_buffer; + if (buf->num_pkt >= MAX_PACKETS || buf->cur_buffer + data_len + 1 > MAX_BYTES) + goto out_of_space; + if (buf->read_pkt == buf->num_pkt) + sd_conv_def_reset(sd); + assert(buf->read_pkt == 0); // no mixing of reading/adding allowed + struct demux_packet *pkt = &buf->pkt[buf->num_pkt++]; + *pkt = (struct demux_packet) { + .buffer = &buf->buffer[buf->cur_buffer], + .len = data_len, + .pts = pts, + .duration = duration, + }; + memcpy(pkt->buffer, data, data_len); + pkt->buffer[data_len] = 0; + buf->cur_buffer += data_len + 1; + return; + +out_of_space: + mp_msg(MSGT_OSD, MSGL_ERR, "Subtitle too big.\n"); +} + +struct demux_packet *sd_conv_def_get_converted(struct sd *sd) +{ + struct sd_conv_buffer *buf = sd->sd_conv_buffer; + if (buf && buf->read_pkt < buf->num_pkt) + return &buf->pkt[buf->read_pkt++]; + return NULL; +} + +void sd_conv_def_reset(struct sd *sd) +{ + struct sd_conv_buffer *buf = sd->sd_conv_buffer; + if (buf) { + buf->read_pkt = buf->num_pkt = 0; + buf->cur_buffer = 0; + } } diff --git a/sub/dec_sub.h b/sub/dec_sub.h index 39632d21a9..805a87ef5c 100644 --- a/sub/dec_sub.h +++ b/sub/dec_sub.h @@ -35,7 +35,7 @@ bool sub_has_get_text(struct dec_sub *sub); char *sub_get_text(struct dec_sub *sub, double pts); void sub_reset(struct dec_sub *sub); -struct sd *sub_get_sd(struct dec_sub *sub); +struct sd *sub_get_last_sd(struct dec_sub *sub); #ifdef CONFIG_ASS struct ass_track *sub_get_ass_track(struct dec_sub *sub); diff --git a/sub/sd.h b/sub/sd.h index 42f7b8a445..fadfe55edc 100644 --- a/sub/sd.h +++ b/sub/sd.h @@ -26,6 +26,14 @@ struct sd { // Shared renderer for ASS - done to avoid reloading embedded fonts. struct ass_library *ass_library; struct ass_renderer *ass_renderer; + + // Set by sub converter + const char *output_codec; + char *output_extradata; + int output_extradata_len; + + // Internal buffer for sd_conv_* functions + struct sd_conv_buffer *sd_conv_buffer; }; struct sd_functions { @@ -33,11 +41,22 @@ struct sd_functions { bool (*supports_format)(const char *format); int (*init)(struct sd *sd); void (*decode)(struct sd *sd, struct demux_packet *packet); + void (*reset)(struct sd *sd); + void (*uninit)(struct sd *sd); + + // decoder void (*get_bitmaps)(struct sd *sd, struct mp_osd_res dim, double pts, struct sub_bitmaps *res); char *(*get_text)(struct sd *sd, double pts); - void (*reset)(struct sd *sd); - void (*uninit)(struct sd *sd); + + // converter + struct demux_packet *(*get_converted)(struct sd *sd); }; +void sd_conv_add_packet(struct sd *sd, void *data, int data_len, double pts, + double duration); +struct demux_packet *sd_conv_def_get_converted(struct sd *sd); +void sd_conv_def_reset(struct sd *sd); +void sd_conv_def_uninit(struct sd *sd); + #endif diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 2ebd2164be..78adbf4863 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -53,8 +53,7 @@ static bool is_text_sub(const char *t) { return t && (is_ass_sub(t) || strcmp(t, "text") == 0 || - strcmp(t, "subrip") == 0 || - strcmp(t, "mov_text") == 0); + strcmp(t, "subrip") == 0); } static bool supports_format(const char *format) @@ -296,7 +295,7 @@ const struct sd_functions sd_ass = { struct ass_track *sub_get_ass_track(struct dec_sub *sub) { - struct sd *sd = sub_get_sd(sub); + struct sd *sd = sub_get_last_sd(sub); if (sd && sd->driver == &sd_ass && sd->priv) { struct sd_ass_priv *ctx = sd->priv; return ctx->ass_track; diff --git a/sub/sd_movtext.c b/sub/sd_movtext.c new file mode 100644 index 0000000000..30d2da8254 --- /dev/null +++ b/sub/sd_movtext.c @@ -0,0 +1,54 @@ +/* + * 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 . + */ + +#include +#include + +#include +#include + +#include "sd.h" + +static bool supports_format(const char *format) +{ + return format && strcmp(format, "mov_text") == 0; +} + +static int init(struct sd *sd) +{ + sd->output_codec = "text"; + return 0; +} + +static void decode(struct sd *sd, struct demux_packet *packet) +{ + unsigned char *data = packet->buffer; + int len = packet->len; + if (len < 2) + return; + len = FFMIN(len - 2, AV_RB16(data)); + data += 2; + sd_conv_add_packet(sd, data, len, packet->pts, packet->duration); +} + +const struct sd_functions sd_movtext = { + .supports_format = supports_format, + .init = init, + .decode = decode, + .get_converted = sd_conv_def_get_converted, + .reset = sd_conv_def_reset, +}; From 14dd95154820d4ec9afb5200335177b011233049 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 1 Jun 2013 19:50:46 +0200 Subject: [PATCH 12/28] sub: split subassconvert.c into sd_microdvd.c and sd_srt.c --- Makefile | 3 +- sub/sd_microdvd.c | 311 ++++++++++++++++++++++++++++++ sub/{subassconvert.c => sd_srt.c} | 256 ------------------------ 3 files changed, 313 insertions(+), 257 deletions(-) create mode 100644 sub/sd_microdvd.c rename sub/{subassconvert.c => sd_srt.c} (70%) diff --git a/Makefile b/Makefile index 583eaf87ec..d3e203a852 100644 --- a/Makefile +++ b/Makefile @@ -231,11 +231,12 @@ SOURCES = talloc.c \ sub/find_subfiles.c \ sub/img_convert.c \ sub/sd_lavc.c \ + sub/sd_microdvd.c \ sub/sd_movtext.c \ sub/sd_spu.c \ + sub/sd_srt.c \ sub/spudec.c \ sub/sub.c \ - sub/subassconvert.c \ sub/subreader.c \ video/csputils.c \ video/fmt-conversion.c \ diff --git a/sub/sd_microdvd.c b/sub/sd_microdvd.c new file mode 100644 index 0000000000..c6c8745e32 --- /dev/null +++ b/sub/sd_microdvd.c @@ -0,0 +1,311 @@ +/* + * Subtitles converter to SSA/ASS in order to allow special formatting + * + * 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 + +#include "core/mp_msg.h" +#include "subassconvert.h" +#include "core/bstr.h" +#include "libavutil/common.h" + +struct line { + char *buf; + int bufsize; + int len; +}; + +#ifdef __GNUC__ +static void append_text(struct line *dst, char *fmt, ...) __attribute__ ((format(printf, 2, 3))); +#endif + +static void append_text(struct line *dst, char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + int ret = vsnprintf(dst->buf + dst->len, dst->bufsize - dst->len, fmt, va); + if (ret < 0) + goto out; + dst->len += ret; + if (dst->len > dst->bufsize) + dst->len = dst->bufsize; + out: + va_end(va); +} + +static int indexof(const char *s, int c) +{ + char *f = strchr(s, c); + return f ? (f - s) : -1; +} + + +/* + * MicroDVD + * + * Based on the specifications found here: + * https://trac.videolan.org/vlc/ticket/1825#comment:6 + */ + +struct microdvd_tag { + char key; + int persistent; + uint32_t data1; + uint32_t data2; + struct bstr data_string; +}; + +#define MICRODVD_PERSISTENT_OFF 0 +#define MICRODVD_PERSISTENT_ON 1 +#define MICRODVD_PERSISTENT_OPENED 2 + +// Color, Font, Size, cHarset, stYle, Position, cOordinate +#define MICRODVD_TAGS "cfshyYpo" + +static void microdvd_set_tag(struct microdvd_tag *tags, struct microdvd_tag tag) +{ + int tag_index = indexof(MICRODVD_TAGS, tag.key); + + if (tag_index < 0) + return; + memcpy(&tags[tag_index], &tag, sizeof(tag)); +} + +// italic, bold, underline, strike-through +#define MICRODVD_STYLES "ibus" + +static char *microdvd_load_tags(struct microdvd_tag *tags, char *s) +{ + while (*s == '{') { + char *start = s; + char tag_char = *(s + 1); + struct microdvd_tag tag = {0}; + + if (!tag_char || *(s + 2) != ':') + break; + s += 3; + + switch (tag_char) { + + /* Style */ + case 'Y': + tag.persistent = MICRODVD_PERSISTENT_ON; + case 'y': + while (*s && *s != '}') { + int style_index = indexof(MICRODVD_STYLES, *s); + + if (style_index >= 0) + tag.data1 |= (1 << style_index); + s++; + } + if (*s != '}') + break; + /* We must distinguish persistent and non-persistent styles + * to handle this kind of style tags: {y:ib}{Y:us} */ + tag.key = tag_char; + break; + + /* Color */ + case 'C': + tag.persistent = MICRODVD_PERSISTENT_ON; + case 'c': + tag.data1 = strtol(s, &s, 16) & 0x00ffffff; + if (*s != '}') + break; + tag.key = 'c'; + break; + + /* Font name */ + case 'F': + tag.persistent = MICRODVD_PERSISTENT_ON; + case 'f': + { + int len = indexof(s, '}'); + if (len < 0) + break; + tag.data_string.start = s; + tag.data_string.len = len; + s += len; + tag.key = 'f'; + break; + } + + /* Font size */ + case 'S': + tag.persistent = MICRODVD_PERSISTENT_ON; + case 's': + tag.data1 = strtol(s, &s, 10); + if (*s != '}') + break; + tag.key = 's'; + break; + + /* Charset */ + case 'H': + { + //TODO: not yet handled, just parsed. + int len = indexof(s, '}'); + if (len < 0) + break; + tag.data_string.start = s; + tag.data_string.len = len; + s += len; + tag.key = 'h'; + break; + } + + /* Position */ + case 'P': + tag.persistent = MICRODVD_PERSISTENT_ON; + tag.data1 = (*s++ == '1'); + if (*s != '}') + break; + tag.key = 'p'; + break; + + /* Coordinates */ + case 'o': + tag.persistent = MICRODVD_PERSISTENT_ON; + tag.data1 = strtol(s, &s, 10); + if (*s != ',') + break; + s++; + tag.data2 = strtol(s, &s, 10); + if (*s != '}') + break; + tag.key = 'o'; + break; + + default: /* Unknown tag, we consider it to be text */ + break; + } + + if (tag.key == 0) + return start; + + microdvd_set_tag(tags, tag); + s++; + } + return s; +} + +static void microdvd_open_tags(struct line *new_line, struct microdvd_tag *tags) +{ + for (int i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) { + if (tags[i].persistent == MICRODVD_PERSISTENT_OPENED) + continue; + switch (tags[i].key) { + case 'Y': + case 'y': + for (int sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++) + if (tags[i].data1 & (1 << sidx)) + append_text(new_line, "{\\%c1}", MICRODVD_STYLES[sidx]); + break; + + case 'c': + append_text(new_line, "{\\c&H%06X&}", tags[i].data1); + break; + + case 'f': + append_text(new_line, "{\\fn%.*s}", BSTR_P(tags[i].data_string)); + break; + + case 's': + append_text(new_line, "{\\fs%d}", tags[i].data1); + break; + + case 'p': + if (tags[i].data1 == 0) + append_text(new_line, "{\\an8}"); + break; + + case 'o': + append_text(new_line, "{\\pos(%d,%d)}", + tags[i].data1, tags[i].data2); + break; + } + if (tags[i].persistent == MICRODVD_PERSISTENT_ON) + tags[i].persistent = MICRODVD_PERSISTENT_OPENED; + } +} + +static void microdvd_close_no_persistent_tags(struct line *new_line, + struct microdvd_tag *tags) +{ + int i; + + for (i = sizeof(MICRODVD_TAGS) - 2; i; i--) { + if (tags[i].persistent != MICRODVD_PERSISTENT_OFF) + continue; + switch (tags[i].key) { + + case 'y': + for (int sidx = sizeof(MICRODVD_STYLES) - 2; sidx >= 0; sidx--) + if (tags[i].data1 & (1 << sidx)) + append_text(new_line, "{\\%c0}", MICRODVD_STYLES[sidx]); + break; + + case 'c': + append_text(new_line, "{\\c}"); + break; + + case 'f': + append_text(new_line, "{\\fn}"); + break; + + case 's': + append_text(new_line, "{\\fs}"); + break; + } + tags[i].key = 0; + } +} + +void subassconvert_microdvd(const char *orig, char *dest, int dest_buffer_size) +{ + /* line is not const to avoid warnings with strtol, etc. + * orig content won't be changed */ + char *line = (char *)orig; + struct line new_line = { + .buf = dest, + .bufsize = dest_buffer_size, + }; + struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}}; + + while (*line) { + line = microdvd_load_tags(tags, line); + microdvd_open_tags(&new_line, tags); + + while (*line && *line != '|') + new_line.buf[new_line.len++] = *line++; + + if (*line == '|') { + microdvd_close_no_persistent_tags(&new_line, tags); + append_text(&new_line, "\\N"); + line++; + } + } + new_line.buf[new_line.len] = 0; +} diff --git a/sub/subassconvert.c b/sub/sd_srt.c similarity index 70% rename from sub/subassconvert.c rename to sub/sd_srt.c index c665750682..fd1d252924 100644 --- a/sub/subassconvert.c +++ b/sub/sd_srt.c @@ -60,13 +60,6 @@ static void append_text_n(struct line *dst, char *start, size_t length) append_text(dst, "%.*s", (int)length, start); } -static int indexof(const char *s, int c) -{ - char *f = strchr(s, c); - return f ? (f - s) : -1; -} - - /* * SubRip @@ -443,252 +436,3 @@ void subassconvert_subrip(const char *orig, char *dest, int dest_buffer_size) } new_line.buf[new_line.len] = 0; } - - -/* - * MicroDVD - * - * Based on the specifications found here: - * https://trac.videolan.org/vlc/ticket/1825#comment:6 - */ - -struct microdvd_tag { - char key; - int persistent; - uint32_t data1; - uint32_t data2; - struct bstr data_string; -}; - -#define MICRODVD_PERSISTENT_OFF 0 -#define MICRODVD_PERSISTENT_ON 1 -#define MICRODVD_PERSISTENT_OPENED 2 - -// Color, Font, Size, cHarset, stYle, Position, cOordinate -#define MICRODVD_TAGS "cfshyYpo" - -static void microdvd_set_tag(struct microdvd_tag *tags, struct microdvd_tag tag) -{ - int tag_index = indexof(MICRODVD_TAGS, tag.key); - - if (tag_index < 0) - return; - memcpy(&tags[tag_index], &tag, sizeof(tag)); -} - -// italic, bold, underline, strike-through -#define MICRODVD_STYLES "ibus" - -static char *microdvd_load_tags(struct microdvd_tag *tags, char *s) -{ - while (*s == '{') { - char *start = s; - char tag_char = *(s + 1); - struct microdvd_tag tag = {0}; - - if (!tag_char || *(s + 2) != ':') - break; - s += 3; - - switch (tag_char) { - - /* Style */ - case 'Y': - tag.persistent = MICRODVD_PERSISTENT_ON; - case 'y': - while (*s && *s != '}') { - int style_index = indexof(MICRODVD_STYLES, *s); - - if (style_index >= 0) - tag.data1 |= (1 << style_index); - s++; - } - if (*s != '}') - break; - /* We must distinguish persistent and non-persistent styles - * to handle this kind of style tags: {y:ib}{Y:us} */ - tag.key = tag_char; - break; - - /* Color */ - case 'C': - tag.persistent = MICRODVD_PERSISTENT_ON; - case 'c': - tag.data1 = strtol(s, &s, 16) & 0x00ffffff; - if (*s != '}') - break; - tag.key = 'c'; - break; - - /* Font name */ - case 'F': - tag.persistent = MICRODVD_PERSISTENT_ON; - case 'f': - { - int len = indexof(s, '}'); - if (len < 0) - break; - tag.data_string.start = s; - tag.data_string.len = len; - s += len; - tag.key = 'f'; - break; - } - - /* Font size */ - case 'S': - tag.persistent = MICRODVD_PERSISTENT_ON; - case 's': - tag.data1 = strtol(s, &s, 10); - if (*s != '}') - break; - tag.key = 's'; - break; - - /* Charset */ - case 'H': - { - //TODO: not yet handled, just parsed. - int len = indexof(s, '}'); - if (len < 0) - break; - tag.data_string.start = s; - tag.data_string.len = len; - s += len; - tag.key = 'h'; - break; - } - - /* Position */ - case 'P': - tag.persistent = MICRODVD_PERSISTENT_ON; - tag.data1 = (*s++ == '1'); - if (*s != '}') - break; - tag.key = 'p'; - break; - - /* Coordinates */ - case 'o': - tag.persistent = MICRODVD_PERSISTENT_ON; - tag.data1 = strtol(s, &s, 10); - if (*s != ',') - break; - s++; - tag.data2 = strtol(s, &s, 10); - if (*s != '}') - break; - tag.key = 'o'; - break; - - default: /* Unknown tag, we consider it to be text */ - break; - } - - if (tag.key == 0) - return start; - - microdvd_set_tag(tags, tag); - s++; - } - return s; -} - -static void microdvd_open_tags(struct line *new_line, struct microdvd_tag *tags) -{ - for (int i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) { - if (tags[i].persistent == MICRODVD_PERSISTENT_OPENED) - continue; - switch (tags[i].key) { - case 'Y': - case 'y': - for (int sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++) - if (tags[i].data1 & (1 << sidx)) - append_text(new_line, "{\\%c1}", MICRODVD_STYLES[sidx]); - break; - - case 'c': - append_text(new_line, "{\\c&H%06X&}", tags[i].data1); - break; - - case 'f': - append_text(new_line, "{\\fn%.*s}", BSTR_P(tags[i].data_string)); - break; - - case 's': - append_text(new_line, "{\\fs%d}", tags[i].data1); - break; - - case 'p': - if (tags[i].data1 == 0) - append_text(new_line, "{\\an8}"); - break; - - case 'o': - append_text(new_line, "{\\pos(%d,%d)}", - tags[i].data1, tags[i].data2); - break; - } - if (tags[i].persistent == MICRODVD_PERSISTENT_ON) - tags[i].persistent = MICRODVD_PERSISTENT_OPENED; - } -} - -static void microdvd_close_no_persistent_tags(struct line *new_line, - struct microdvd_tag *tags) -{ - int i; - - for (i = sizeof(MICRODVD_TAGS) - 2; i; i--) { - if (tags[i].persistent != MICRODVD_PERSISTENT_OFF) - continue; - switch (tags[i].key) { - - case 'y': - for (int sidx = sizeof(MICRODVD_STYLES) - 2; sidx >= 0; sidx--) - if (tags[i].data1 & (1 << sidx)) - append_text(new_line, "{\\%c0}", MICRODVD_STYLES[sidx]); - break; - - case 'c': - append_text(new_line, "{\\c}"); - break; - - case 'f': - append_text(new_line, "{\\fn}"); - break; - - case 's': - append_text(new_line, "{\\fs}"); - break; - } - tags[i].key = 0; - } -} - -void subassconvert_microdvd(const char *orig, char *dest, int dest_buffer_size) -{ - /* line is not const to avoid warnings with strtol, etc. - * orig content won't be changed */ - char *line = (char *)orig; - struct line new_line = { - .buf = dest, - .bufsize = dest_buffer_size, - }; - struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}}; - - while (*line) { - line = microdvd_load_tags(tags, line); - microdvd_open_tags(&new_line, tags); - - while (*line && *line != '|') - new_line.buf[new_line.len++] = *line++; - - if (*line == '|') { - microdvd_close_no_persistent_tags(&new_line, tags); - append_text(&new_line, "\\N"); - line++; - } - } - new_line.buf[new_line.len] = 0; -} From e19ffa02aa370cbc3b559f85b286ea09b06ab29b Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 1 Jun 2013 19:54:18 +0200 Subject: [PATCH 13/28] sub: turn subassconvert_ functions into sub converters This means subassconvert.c is split in sd_srt.c and sd_microdvd.c. Now this code is involved in the sub conversion chain like sd_movtext is. The invocation of the converter in sd_ass.c is removed. This requires some other changes to make the new sub converter code work with loading external subtitles. Until now, subtitles loaded via subreader.c was assumed to be in plaintext, or for some formats, in ASS (except in -no-ass mode). Then these were added to an ASS_Track. Change this so that subtitles are always in their original format (as far as decoders/converters for them are available), and turn every sub event read by subreader.c as packet to the dec_sub.c subtitle chain. This removes differences between external/demuxed and -ass/-no-ass code paths further. --- core/mplayer.c | 35 +++++++--------- demux/stheader.h | 5 ++- sub/ass_mp.c | 79 ---------------------------------- sub/ass_mp.h | 2 - sub/dec_sub.c | 67 ++++++++++++++++++++++++++--- sub/sd.h | 2 + sub/sd_ass.c | 38 +++++++---------- sub/sd_microdvd.c | 34 +++++++++++++-- sub/sd_srt.c | 34 +++++++++++++-- sub/subassconvert.h | 27 ------------ sub/subreader.c | 100 +++++--------------------------------------- sub/subreader.h | 1 + 12 files changed, 169 insertions(+), 255 deletions(-) delete mode 100644 sub/subassconvert.h diff --git a/core/mplayer.c b/core/mplayer.c index 1fae2eece8..de23e97a49 100644 --- a/core/mplayer.c +++ b/core/mplayer.c @@ -1036,10 +1036,12 @@ static void add_dvd_tracks(struct MPContext *mpctx) } #ifdef CONFIG_ASS -static int free_ass_track(void *ptr) +static int free_sub_data(void *ptr) { - struct ass_track *track = *(struct ass_track **)ptr; - ass_free_track(track); + struct sh_sub *sh_sub = *(struct sh_sub **)ptr; + if (sh_sub->track) + ass_free_track(sh_sub->track); + talloc_free(sh_sub->sub_data); return 1; } #endif @@ -1049,7 +1051,7 @@ struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename, { struct MPOpts *opts = &mpctx->opts; struct ass_track *asst = NULL; - const char *codec = NULL; + sub_data *subd = NULL; if (filename == NULL) return NULL; @@ -1059,30 +1061,23 @@ struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename, // through sd_ass makes the code much simpler, as sd_ass can handle all // the weird special-cases. #ifdef CONFIG_ASS - if (opts->ass_enabled) { + if (opts->ass_enabled) asst = mp_ass_read_stream(mpctx->ass_library, filename, opts->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) { + if (!asst) + subd = sub_read_file(filename, fps, &mpctx->opts); + if (asst || subd) { struct demuxer *d = new_sub_pseudo_demuxer(opts); assert(d->num_streams == 1); struct sh_stream *s = d->streams[0]; assert(s->type == STREAM_SUB); + s->codec = asst ? "ass" : subd->codec; s->sub->track = asst; - s->codec = codec; + s->sub->sub_data = subd; - struct ass_track **pptr = talloc(d, struct ass_track*); - *pptr = asst; - talloc_set_destructor(pptr, free_ass_track); + struct sh_sub **pptr = talloc(d, struct sh_sub*); + *pptr = s->sub; + talloc_set_destructor(pptr, free_sub_data); struct track *t = add_stream_track(mpctx, s, false); t->is_external = true; diff --git a/demux/stheader.h b/demux/stheader.h index 8220d65a61..421dfaf857 100644 --- a/demux/stheader.h +++ b/demux/stheader.h @@ -163,8 +163,9 @@ typedef struct sh_sub { SH_COMMON unsigned char *extradata; // extra header data passed from demuxer int extradata_len; - struct ass_track *track; // loaded by libass - struct dec_sub *dec_sub; // decoder context + struct ass_track *track; // loaded by libass + struct sub_data *sub_data; // loaded by subreader.c + struct dec_sub *dec_sub; // decoder context } sh_sub_t; // demuxer.c: diff --git a/sub/ass_mp.c b/sub/ass_mp.c index 4be89fb004..85105a33ad 100644 --- a/sub/ass_mp.c +++ b/sub/ass_mp.c @@ -102,85 +102,6 @@ ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts) return track; } -/** - * \brief Convert subtitle to ASS_Events for the given track - * \param track track - * \param sub subtitle to convert - * \return event id - * note: assumes that subtitle is _not_ fps-based; caller must manually correct - * Start and Duration in other case. - **/ -static int ass_process_subtitle(ASS_Track *track, subtitle *sub) -{ - int eid; - ASS_Event *event; - int len = 0, j; - char *p; - char *end; - - eid = ass_alloc_event(track); - event = track->events + eid; - - event->Start = sub->start * 10; - event->Duration = (sub->end - sub->start) * 10; - event->Style = track->default_style; - - for (j = 0; j < sub->lines; ++j) - len += sub->text[j] ? strlen(sub->text[j]) : 0; - - len += 2 * sub->lines; // '\N', including the one after the last line - len += 6; // {\anX} - len += 1; // '\0' - - event->Text = malloc(len); - end = event->Text + len; - p = event->Text; - - if (sub->alignment) - p += snprintf(p, end - p, "{\\an%d}", sub->alignment); - - for (j = 0; j < sub->lines; ++j) - p += snprintf(p, end - p, "%s\\N", sub->text[j]); - - if (sub->lines > 0) - p -= 2; // remove last "\N" - *p = 0; - - mp_msg(MSGT_ASS, MSGL_V, - "plaintext event at %" PRId64 ", +%" PRId64 ": %s \n", - (int64_t) event->Start, (int64_t) event->Duration, event->Text); - - return eid; -} - - -/** - * \brief Convert subdata to ASS_Track - * \param subdata subtitles struct from subreader - * \param fps video framerate - * \return newly allocated ASS_Track, filled with subtitles from subdata - */ -ASS_Track *mp_ass_read_subdata(ASS_Library *library, struct MPOpts *opts, - sub_data *subdata, double fps) -{ - ASS_Track *track; - int i; - - track = mp_ass_default_track(library, opts); - track->name = subdata->filename ? strdup(subdata->filename) : 0; - - for (i = 0; i < subdata->sub_num; ++i) { - int eid = ass_process_subtitle(track, subdata->subtitles + i); - if (eid < 0) - continue; - if (!subdata->sub_uses_time) { - track->events[eid].Start *= 100. / fps; - track->events[eid].Duration *= 100. / fps; - } - } - return track; -} - ASS_Track *mp_ass_read_stream(ASS_Library *library, const char *fname, char *charset) { diff --git a/sub/ass_mp.h b/sub/ass_mp.h index 0f67b17fe1..4b31a832a4 100644 --- a/sub/ass_mp.h +++ b/sub/ass_mp.h @@ -47,8 +47,6 @@ struct osd_style_opts; void mp_ass_set_style(ASS_Style *style, struct osd_style_opts *opts); ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts); -ASS_Track *mp_ass_read_subdata(ASS_Library *library, struct MPOpts *opts, - sub_data *subdata, double fps); ASS_Track *mp_ass_read_stream(ASS_Library *library, const char *fname, char *charset); diff --git a/sub/dec_sub.c b/sub/dec_sub.c index 6ef1e4a8cf..49ecd5c009 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -22,9 +22,10 @@ #include "config.h" #include "demux/stheader.h" -#include "sub/sd.h" -#include "sub/sub.h" -#include "sub/dec_sub.h" +#include "sd.h" +#include "sub.h" +#include "dec_sub.h" +#include "subreader.h" #include "core/options.h" #include "core/mp_msg.h" @@ -32,6 +33,8 @@ extern const struct sd_functions sd_ass; extern const struct sd_functions sd_lavc; extern const struct sd_functions sd_spu; extern const struct sd_functions sd_movtext; +extern const struct sd_functions sd_srt; +extern const struct sd_functions sd_microdvd; static const struct sd_functions *sd_list[] = { #ifdef CONFIG_ASS @@ -40,10 +43,12 @@ static const struct sd_functions *sd_list[] = { &sd_lavc, &sd_spu, &sd_movtext, + &sd_srt, + &sd_microdvd, NULL }; -#define MAX_NUM_SD 2 +#define MAX_NUM_SD 3 struct dec_sub { struct MPOpts *opts; @@ -108,6 +113,55 @@ void sub_set_ass_renderer(struct dec_sub *sub, struct ass_library *ass_library, sub->init_sd.ass_renderer = ass_renderer; } +// Subtitles read with subreader.c +static void read_sub_data(struct dec_sub *sub, struct sub_data *subdata) +{ + assert(sub_accept_packets_in_advance(sub)); + char *temp = NULL; + + for (int i = 0; i < subdata->sub_num; i++) { + subtitle *st = &subdata->subtitles[i]; + // subdata is in 10 ms ticks, pts is in seconds + double t = subdata->sub_uses_time ? 0.01 : (1 / subdata->fallback_fps); + + int len = 0; + for (int j = 0; j < st->lines; j++) + len += st->text[j] ? strlen(st->text[j]) : 0; + + len += 2 * st->lines; // '\N', including the one after the last line + len += 6; // {\anX} + len += 1; // '\0' + + if (talloc_get_size(temp) < len) { + talloc_free(temp); + temp = talloc_array(NULL, char, len); + } + + char *p = temp; + char *end = p + len; + + if (st->alignment) + p += snprintf(p, end - p, "{\\an%d}", st->alignment); + + for (int j = 0; j < st->lines; j++) + p += snprintf(p, end - p, "%s\\N", st->text[j]); + + if (st->lines > 0) + p -= 2; // remove last "\N" + *p = 0; + + struct demux_packet pkt = {0}; + pkt.pts = st->start * t; + pkt.duration = (st->end - st->start) * t; + pkt.buffer = temp; + pkt.len = strlen(temp); + + sub_decode(sub, &pkt); + } + + talloc_free(temp); +} + static int sub_init_decoder(struct dec_sub *sub, struct sd *sd) { sd->driver = NULL; @@ -148,8 +202,11 @@ void sub_init_from_sh(struct dec_sub *sub, struct sh_sub *sh) sub->sd[sub->num_sd] = sd; sub->num_sd++; // Try adding new converters until a decoder is reached - if (sd->driver->get_bitmaps || sd->driver->get_text) + if (sd->driver->get_bitmaps || sd->driver->get_text) { + if (sh->sub_data) + read_sub_data(sub, sh->sub_data); return; + } init_sd = (struct sd) { .codec = sd->output_codec, .extradata = sd->output_extradata, diff --git a/sub/sd.h b/sub/sd.h index fadfe55edc..dbb6af835f 100644 --- a/sub/sd.h +++ b/sub/sd.h @@ -59,4 +59,6 @@ struct demux_packet *sd_conv_def_get_converted(struct sd *sd); void sd_conv_def_reset(struct sd *sd); void sd_conv_def_uninit(struct sd *sd); +#define SD_MAX_LINE_LEN 1000 + #endif diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 78adbf4863..405cef323a 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -27,12 +27,10 @@ #include "core/options.h" #include "core/mp_common.h" #include "core/mp_msg.h" -#include "demux/stheader.h" #include "sub.h" #include "dec_sub.h" #include "ass_mp.h" #include "sd.h" -#include "subassconvert.h" struct sd_ass_priv { struct ass_track *ass_track; @@ -43,22 +41,17 @@ struct sd_ass_priv { char last_text[500]; }; -static bool is_ass_sub(const char *t) +static bool is_native_ass(const char *t) { - return t && (strcmp(t, "ass") == 0 || - strcmp(t, "ssa") == 0); -} - -static bool is_text_sub(const char *t) -{ - return t && (is_ass_sub(t) || - strcmp(t, "text") == 0 || - strcmp(t, "subrip") == 0); + return strcmp(t, "ass") == 0 || strcmp(t, "ssa") == 0; } static bool supports_format(const char *format) { - return is_text_sub(format); + // ass-text is produced by converters and the subreader.c ssa parser; this + // format has ASS tags, but doesn't start with any prelude, nor does it + // have extradata. + return format && (is_native_ass(format) || strcmp(format, "ass-text") == 0); } static void free_last_event(ASS_Track *track) @@ -73,19 +66,21 @@ static int init(struct sd *sd) if (!sd->ass_library || !sd->ass_renderer) return -1; - bool ass = is_ass_sub(sd->codec); + bool ass = is_native_ass(sd->codec); struct sd_ass_priv *ctx = talloc_zero(NULL, struct sd_ass_priv); sd->priv = ctx; if (sd->ass_track) { ctx->ass_track = sd->ass_track; } else if (ass) { ctx->ass_track = ass_new_track(sd->ass_library); - if (sd->extradata) - ass_process_codec_private(ctx->ass_track, sd->extradata, - sd->extradata_len); } else ctx->ass_track = mp_ass_default_track(sd->ass_library, sd->opts); + if (sd->extradata) { + ass_process_codec_private(ctx->ass_track, sd->extradata, + sd->extradata_len); + } + ctx->vsfilter_aspect = ass; return 0; } @@ -99,8 +94,7 @@ static void decode(struct sd *sd, struct demux_packet *packet) unsigned char *text = data; struct sd_ass_priv *ctx = sd->priv; ASS_Track *track = ctx->ass_track; - - if (is_ass_sub(sd->codec)) { + if (is_native_ass(sd->codec)) { if (bstr_startswith0((bstr){data, data_len}, "Dialogue: ")) { // broken ffmpeg ASS packet format ctx->flush_on_seek = true; @@ -138,12 +132,10 @@ static void decode(struct sd *sd, struct demux_packet *packet) return; } not_all_whitespace:; - char buf[500]; - subassconvert_subrip(text, buf, sizeof(buf)); for (int i = 0; i < track->n_events; i++) if (track->events[i].Start == ipts && (duration <= 0 || track->events[i].Duration == iduration) - && strcmp(track->events[i].Text, buf) == 0) + && strcmp(track->events[i].Text, text) == 0) return; // We've already added this subtitle if (duration <= 0) { iduration = 10000; @@ -154,7 +146,7 @@ static void decode(struct sd *sd, struct demux_packet *packet) event->Start = ipts; event->Duration = iduration; event->Style = track->default_style; - event->Text = strdup(buf); + event->Text = strdup(text); } static void get_bitmaps(struct sd *sd, struct mp_osd_res dim, double pts, diff --git a/sub/sd_microdvd.c b/sub/sd_microdvd.c index c6c8745e32..eba5b67576 100644 --- a/sub/sd_microdvd.c +++ b/sub/sd_microdvd.c @@ -25,11 +25,11 @@ #include #include #include +#include #include "core/mp_msg.h" -#include "subassconvert.h" #include "core/bstr.h" -#include "libavutil/common.h" +#include "sd.h" struct line { char *buf; @@ -61,7 +61,6 @@ static int indexof(const char *s, int c) return f ? (f - s) : -1; } - /* * MicroDVD * @@ -283,7 +282,7 @@ static void microdvd_close_no_persistent_tags(struct line *new_line, } } -void subassconvert_microdvd(const char *orig, char *dest, int dest_buffer_size) +static void convert_microdvd(const char *orig, char *dest, int dest_buffer_size) { /* line is not const to avoid warnings with strtol, etc. * orig content won't be changed */ @@ -309,3 +308,30 @@ void subassconvert_microdvd(const char *orig, char *dest, int dest_buffer_size) } new_line.buf[new_line.len] = 0; } + +static bool supports_format(const char *format) +{ + return format && strcmp(format, "microdvd") == 0; +} + +static int init(struct sd *sd) +{ + sd->output_codec = "ass-text"; + return 0; +} + +static void decode(struct sd *sd, struct demux_packet *packet) +{ + char dest[SD_MAX_LINE_LEN]; + // Assume input buffer is padded with 0 + convert_microdvd(packet->buffer, dest, sizeof(dest)); + sd_conv_add_packet(sd, dest, strlen(dest), packet->pts, packet->duration); +} + +const struct sd_functions sd_microdvd = { + .supports_format = supports_format, + .init = init, + .decode = decode, + .get_converted = sd_conv_def_get_converted, + .reset = sd_conv_def_reset, +}; diff --git a/sub/sd_srt.c b/sub/sd_srt.c index fd1d252924..ec4768a598 100644 --- a/sub/sd_srt.c +++ b/sub/sd_srt.c @@ -25,11 +25,11 @@ #include #include #include +#include #include "core/mp_msg.h" -#include "subassconvert.h" #include "core/bstr.h" -#include "libavutil/common.h" +#include "sd.h" struct line { char *buf; @@ -273,7 +273,7 @@ static int read_attr(char **s, struct bstr *attr, struct bstr *val) return 0; } -void subassconvert_subrip(const char *orig, char *dest, int dest_buffer_size) +static void convert_subrip(const char *orig, char *dest, int dest_buffer_size) { /* line is not const to avoid warnings with strtol, etc. * orig content won't be changed */ @@ -436,3 +436,31 @@ void subassconvert_subrip(const char *orig, char *dest, int dest_buffer_size) } new_line.buf[new_line.len] = 0; } + +static bool supports_format(const char *format) +{ + return format && (strcmp(format, "subrip") == 0 || + strcmp(format, "text") == 0); +} + +static int init(struct sd *sd) +{ + sd->output_codec = "ass-text"; + return 0; +} + +static void decode(struct sd *sd, struct demux_packet *packet) +{ + char dest[SD_MAX_LINE_LEN]; + // Assume input buffer is padded with 0 + convert_subrip(packet->buffer, dest, sizeof(dest)); + sd_conv_add_packet(sd, dest, strlen(dest), packet->pts, packet->duration); +} + +const struct sd_functions sd_srt = { + .supports_format = supports_format, + .init = init, + .decode = decode, + .get_converted = sd_conv_def_get_converted, + .reset = sd_conv_def_reset, +}; diff --git a/sub/subassconvert.h b/sub/subassconvert.h deleted file mode 100644 index e6a4425198..0000000000 --- a/sub/subassconvert.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Header for subtitles converter to SSA/ASS - * - * 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. - */ - -#ifndef MPLAYER_SUBASSCONVERT_H -#define MPLAYER_SUBASSCONVERT_H - -void subassconvert_subrip(const char *orig, char *dest, int dest_buffer_size); -void subassconvert_microdvd(const char *orig, char *dest, int dest_buffer_size); - -#endif diff --git a/sub/subreader.c b/sub/subreader.c index 12da7d8f3b..8c5a259196 100644 --- a/sub/subreader.c +++ b/sub/subreader.c @@ -32,7 +32,6 @@ #include "core/mp_msg.h" #include "subreader.h" #include "core/mp_common.h" -#include "subassconvert.h" #include "core/options.h" #include "stream/stream.h" #include "libavutil/common.h" @@ -299,7 +298,6 @@ static subtitle *sub_read_line_microdvd(stream_t *st,subtitle *current, int utf16 = args->utf16; char line[LINE_LEN+1]; char line2[LINE_LEN+1]; - char *p; do { if (!stream_read_line (st, line, LINE_LEN, utf16)) return NULL; @@ -310,13 +308,7 @@ static subtitle *sub_read_line_microdvd(stream_t *st,subtitle *current, "{%ld}{%ld}%[^\r\n]", &(current->start), &(current->end), line2) < 3)); - if (args->opts->ass_enabled) { - subassconvert_microdvd(line2, line, LINE_LEN + 1); - p = line; - } else - p = line2; - - return set_multiline_text(current, p, 0); + return set_multiline_text(current, line2, 0); } static subtitle *sub_read_line_mpl2(stream_t *st,subtitle *current, @@ -370,8 +362,8 @@ static subtitle *sub_read_line_subrip(stream_t* st, subtitle *current, return current; } -static subtitle *sub_ass_read_line_subviewer(stream_t *st, subtitle *current, - struct readline_args *args) +static subtitle *sub_read_line_subviewer(stream_t *st, subtitle *current, + struct readline_args *args) { int utf16 = args->utf16; int a1, a2, a3, a4, b1, b2, b3, b4, j = 0; @@ -417,74 +409,14 @@ static subtitle *sub_ass_read_line_subviewer(stream_t *st, subtitle *current, j += len; } - /* Use the ASS/SSA converter to transform the whole lines */ if (full_line[0]) { - char converted_line[LINE_LEN + 1]; - subassconvert_subrip(full_line, converted_line, LINE_LEN + 1); - current->text[0] = strdup(converted_line); + current->text[0] = strdup(full_line); current->lines = 1; } } return current; } -static subtitle *sub_read_line_subviewer(stream_t *st,subtitle *current, - struct readline_args *args) -{ - int utf16 = args->utf16; - char line[LINE_LEN+1]; - int a1,a2,a3,a4,b1,b2,b3,b4; - char *p=NULL; - int i,len; - - if (args->opts->ass_enabled) - return sub_ass_read_line_subviewer(st, current, args); - while (!current->text[0]) { - if (!stream_read_line (st, line, LINE_LEN, utf16)) return NULL; - if ((len=sscanf (line, "%d:%d:%d%*1[,.:]%d --> %d:%d:%d%*1[,.:]%d",&a1,&a2,&a3,&a4,&b1,&b2,&b3,&b4)) < 8) - continue; - current->start = a1*360000+a2*6000+a3*100+a4/10; - current->end = b1*360000+b2*6000+b3*100+b4/10; - for (i=0; itext[i]=malloc (len+1); - if (!current->text[i]) return ERR; - //strncpy (current->text[i], line, len); current->text[i][len]='\0'; - for(; j') { - skip=0; - continue; - } - if(line[j]=='<') { - skip=1; - continue; - } - if(skip) { - continue; - } - *curptr=line[j]; - curptr++; - } - *curptr='\0'; - - i++; - } else { - break; - } - } - current->lines=i; - } - return current; -} - static subtitle *sub_read_line_subviewer2(stream_t *st,subtitle *current, struct readline_args *args) { @@ -675,20 +607,6 @@ static subtitle *sub_read_line_ssa(stream_t *st,subtitle *current, return current; } -static void sub_pp_ssa(subtitle *sub) -{ - for (int i = 0; i < sub->lines; i++) { - char *s, *d; - s = d = sub->text[i]; - while (1) { - while (*s == '{') - while (*s && *s++ != '}'); - if (!(*d++ = *s++)) - break; - } - } -} - /* * PJS subtitles reader. * That's the "Phoenix Japanimation Society" format. @@ -1238,6 +1156,7 @@ struct subreader { struct readline_args *args); void (*post)(subtitle *dest); const char *name; + const char *codec_name; }; #ifdef CONFIG_ENCA @@ -1313,13 +1232,13 @@ sub_data* sub_read_file(char *filename, float fps, struct MPOpts *opts) int uses_time = 0, sub_num = 0, sub_errs = 0; static const struct subreader sr[]= { - { sub_read_line_microdvd, NULL, "microdvd" }, + { sub_read_line_microdvd, NULL, "microdvd", "microdvd" }, { sub_read_line_subrip, NULL, "subviewer" }, - { sub_read_line_subviewer, NULL, "subrip" }, + { sub_read_line_subviewer, NULL, "subrip", "subrip" }, { sub_read_line_sami, NULL, "sami" }, { sub_read_line_vplayer, NULL, "vplayer" }, { sub_read_line_rt, NULL, "rt" }, - { sub_read_line_ssa, sub_pp_ssa, "ssa" }, + { sub_read_line_ssa, NULL, "ssa", "ass-text" }, { sub_read_line_pjs, NULL, "pjs" }, { sub_read_line_mpsub, NULL, "mpsub" }, { sub_read_line_aqt, NULL, "aqt" }, @@ -1678,12 +1597,13 @@ if ((opts->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 = "text"; //srp->name; + subt_data->codec = srp->codec_name ? srp->codec_name : "text"; subt_data->filename = strdup(filename); subt_data->sub_uses_time = uses_time; subt_data->sub_num = sub_num; subt_data->sub_errs = sub_errs; subt_data->subtitles = return_sub; + subt_data->fallback_fps = fps; return subt_data; } diff --git a/sub/subreader.h b/sub/subreader.h index c62dd5ddd2..3b2e53efd8 100644 --- a/sub/subreader.h +++ b/sub/subreader.h @@ -70,6 +70,7 @@ typedef struct sub_data { int sub_uses_time; int sub_num; // number of subtitle structs int sub_errs; + double fallback_fps; } sub_data; struct MPOpts; From b11bd1fe5e82fb7cd9aa912c2b1c98de8704bb87 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 1 Jun 2013 19:54:31 +0200 Subject: [PATCH 14/28] sub: make use of libavcodec subtitle converters This allows using some formats that were not supported until now, like WebVTT. We still prefer the internal subtitle reader (subreader.c), because 1. Libav, and 2. random things which we probably want to keep, such as control over formatting, codepage stuff, or various mysterious postprecessing done in that code. --- Makefile | 1 + sub/dec_sub.c | 10 ++++ sub/sd.h | 2 + sub/sd_ass.c | 7 +++ sub/sd_lavc_conv.c | 132 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 152 insertions(+) create mode 100644 sub/sd_lavc_conv.c diff --git a/Makefile b/Makefile index d3e203a852..389a519055 100644 --- a/Makefile +++ b/Makefile @@ -231,6 +231,7 @@ SOURCES = talloc.c \ sub/find_subfiles.c \ sub/img_convert.c \ sub/sd_lavc.c \ + sub/sd_lavc_conv.c \ sub/sd_microdvd.c \ sub/sd_movtext.c \ sub/sd_spu.c \ diff --git a/sub/dec_sub.c b/sub/dec_sub.c index 49ecd5c009..ec9af94abb 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -35,6 +35,7 @@ extern const struct sd_functions sd_spu; extern const struct sd_functions sd_movtext; extern const struct sd_functions sd_srt; extern const struct sd_functions sd_microdvd; +extern const struct sd_functions sd_lavc_conv; static const struct sd_functions *sd_list[] = { #ifdef CONFIG_ASS @@ -45,6 +46,7 @@ static const struct sd_functions *sd_list[] = { &sd_movtext, &sd_srt, &sd_microdvd, + &sd_lavc_conv, NULL }; @@ -159,6 +161,14 @@ static void read_sub_data(struct dec_sub *sub, struct sub_data *subdata) sub_decode(sub, &pkt); } + struct sd *sd = sub_get_last_sd(sub); + // Hack for broken FFmpeg packet format: make sd_ass keep the subtitle + // events on reset(), even though broken FFmpeg ASS packets were received + // (from sd_lavc_conv.c). Normally, these events are removed on seek/reset, + // but this is obviously unwanted in this case. + if (sd && sd->driver->fix_events) + sd->driver->fix_events(sd); + talloc_free(temp); } diff --git a/sub/sd.h b/sub/sd.h index dbb6af835f..20dce8003d 100644 --- a/sub/sd.h +++ b/sub/sd.h @@ -44,6 +44,8 @@ struct sd_functions { void (*reset)(struct sd *sd); void (*uninit)(struct sd *sd); + void (*fix_events)(struct sd *sd); + // decoder void (*get_bitmaps)(struct sd *sd, struct mp_osd_res dim, double pts, struct sub_bitmaps *res); diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 405cef323a..d8951df96f 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -254,6 +254,12 @@ static char *get_text(struct sd *sd, double pts) return ctx->last_text; } +static void fix_events(struct sd *sd) +{ + struct sd_ass_priv *ctx = sd->priv; + ctx->flush_on_seek = false; +} + static void reset(struct sd *sd) { struct sd_ass_priv *ctx = sd->priv; @@ -281,6 +287,7 @@ const struct sd_functions sd_ass = { .decode = decode, .get_bitmaps = get_bitmaps, .get_text = get_text, + .fix_events = fix_events, .reset = reset, .uninit = uninit, }; diff --git a/sub/sd_lavc_conv.c b/sub/sd_lavc_conv.c new file mode 100644 index 0000000000..e45e743499 --- /dev/null +++ b/sub/sd_lavc_conv.c @@ -0,0 +1,132 @@ +/* + * 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 . + */ + +#include +#include + +#include +#include +#include + +#include "talloc.h" +#include "core/mp_msg.h" +#include "core/av_common.h" +#include "sd.h" + +struct sd_lavc_priv { + AVCodecContext *avctx; +}; + +static bool supports_format(const char *format) +{ + enum AVCodecID cid = mp_codec_to_av_codec_id(format); + const AVCodecDescriptor *desc = avcodec_descriptor_get(cid); + // These are documented to support AVSubtitleRect->ass. + return desc && (desc->props & AV_CODEC_PROP_TEXT_SUB); +} + +static int init(struct sd *sd) +{ + struct sd_lavc_priv *priv = talloc_zero(NULL, struct sd_lavc_priv); + AVCodecContext *avctx = NULL; + AVCodec *codec = avcodec_find_decoder(mp_codec_to_av_codec_id(sd->codec)); + if (!codec) + goto error; + avctx = avcodec_alloc_context3(codec); + if (!avctx) + goto error; + avctx->extradata_size = sd->extradata_len; + avctx->extradata = sd->extradata; + if (avcodec_open2(avctx, codec, NULL) < 0) + goto error; + // Documented as "set by libavcodec", but there is no other way + avctx->time_base = (AVRational) {1, 1000}; + priv->avctx = avctx; + sd->priv = priv; + sd->output_codec = "ass"; + sd->output_extradata = avctx->subtitle_header; + sd->output_extradata_len = avctx->subtitle_header_size; + return 0; + + error: + mp_msg(MSGT_SUBREADER, MSGL_ERR, + "Could not open libavcodec subtitle converter\n"); + av_free(avctx); + talloc_free(priv); + return -1; +} + +static void decode(struct sd *sd, struct demux_packet *packet) +{ + struct sd_lavc_priv *priv = sd->priv; + AVCodecContext *avctx = priv->avctx; + double ts = av_q2d(av_inv_q(avctx->time_base)); + AVSubtitle sub = {0}; + AVPacket pkt; + int ret, got_sub; + + av_init_packet(&pkt); + pkt.data = packet->buffer; + pkt.size = packet->len; + pkt.pts = packet->pts == MP_NOPTS_VALUE ? AV_NOPTS_VALUE : packet->pts * ts; + pkt.duration = packet->duration * ts; + + ret = avcodec_decode_subtitle2(avctx, &sub, &got_sub, &pkt); + if (ret < 0) { + mp_msg(MSGT_OSD, MSGL_ERR, "Error decoding subtitle\n"); + } else if (got_sub) { + for (int i = 0; i < sub.num_rects; i++) { + char *ass_line = sub.rects[i]->ass; + if (!ass_line) + break; + // This might contain embedded timestamps, using the "old" ffmpeg + // ASS packet format, in which case pts/duration might be ignored + // at a later point. + sd_conv_add_packet(sd, ass_line, strlen(ass_line), + packet->pts, packet->duration); + } + } + + av_free_packet(&pkt); + avsubtitle_free(&sub); +} + +static void reset(struct sd *sd) +{ + struct sd_lavc_priv *priv = sd->priv; + + avcodec_flush_buffers(priv->avctx); + sd_conv_def_reset(sd); +} + +static void uninit(struct sd *sd) +{ + struct sd_lavc_priv *priv = sd->priv; + + avcodec_close(priv->avctx); + av_free(priv->avctx); + talloc_free(priv); +} + +const struct sd_functions sd_lavc_conv = { + .supports_format = supports_format, + .init = init, + .decode = decode, + .get_converted = sd_conv_def_get_converted, + .reset = reset, + .uninit = uninit, +}; From 6dbedd27d53b61744d512e27bb0c3853cf6d7bba Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 1 Jun 2013 19:54:34 +0200 Subject: [PATCH 15/28] demux_lavf: always set packet duration Makes WebVTT actually work. Also simplify the logic for setting duration. Only the subtitle path uses the packet duration, so the checks for STREAM_SUB as well as the keyframe flag are redundant. Apparently duration and convergence_duration are the same thing, but convergence_duration was added as Matroska-specific hack to get a higher value range (int vs. int64_t) with high resolution Matroska timebases. For us it doesn't matter, because double floats are used for timestamps and durations. --- demux/demux_lavf.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c index a978bc9cee..a482f736c6 100644 --- a/demux/demux_lavf.c +++ b/demux/demux_lavf.c @@ -671,11 +671,8 @@ static int demux_lavf_fill_buffer(demuxer_t *demux, demux_stream_t *dsds) if (ts != AV_NOPTS_VALUE) { dp->pts = ts * av_q2d(st->time_base); priv->last_pts = dp->pts * AV_TIME_BASE; - // always set duration for subtitles, even if AV_PKT_FLAG_KEY isn't set, - // otherwise they will stay on screen to long if e.g. ASS is demuxed - // from mkv - if ((stream->type == STREAM_SUB || (pkt->flags & AV_PKT_FLAG_KEY)) && - pkt->convergence_duration > 0) + dp->duration = pkt->duration * av_q2d(st->time_base); + if (pkt->convergence_duration > 0) dp->duration = pkt->convergence_duration * av_q2d(st->time_base); } dp->pos = demux->filepos; From e42a7714133bc0dd0b786af45749ea894f32e6a7 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 2 Jun 2013 19:11:25 +0200 Subject: [PATCH 16/28] sd_ass: strip empty/whitespace lines in -no-ass mode Will just destroy output. In some cases empty newlines might be used by bad scripts for spacing; too bad for them. --- sub/sd_ass.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/sub/sd_ass.c b/sub/sd_ass.c index d8951df96f..21d941933c 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -224,6 +224,16 @@ static void ass_to_plaintext(struct buf *b, const char *in) } } +// Empty string counts as whitespace. Reads s[len-1] even if there are \0s. +static bool is_whitespace_only(char *s, int len) +{ + for (int n = 0; n < len; n++) { + if (s[n] != ' ' && s[n] != '\t') + return false; + } + return true; +} + static char *get_text(struct sd *sd, double pts) { struct sd_ass_priv *ctx = sd->priv; @@ -240,8 +250,10 @@ static char *get_text(struct sd *sd, double pts) double end = (event->Start + event->Duration) / 1000.0; if (pts >= start && pts < end) { if (event->Text) { + int start = b.len; ass_to_plaintext(&b, event->Text); - append(&b, '\n'); + if (!is_whitespace_only(&b.start[b.len], b.len - start)) + append(&b, '\n'); } } } From 74e3ac8bf885dad5b6d33fc8d2c714dd91c18b47 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 2 Jun 2013 19:38:57 +0200 Subject: [PATCH 17/28] sd_lavc_conv: add hack if AV_CODEC_PROP_TEXT_SUB is not available Otherwise this could happily open decoders for image subtitles or even audio/video decoders. AV_CODEC_PROP_TEXT_SUB is a preprocessor symbol, but it's still better to detect this properly instead of using #ifdef, because these flags might as well be changed into enums sooner or later. --- configure | 12 ++++++++++++ sub/sd_lavc_conv.c | 18 +++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/configure b/configure index e6b77f4595..0b0913bf8c 100755 --- a/configure +++ b/configure @@ -2684,6 +2684,17 @@ else fi +echocheck "libavcodec AV_CODEC_PROP_TEXT_SUB API" +_avcodec_has_text_flag_api=no +statement_check libavcodec/avcodec.h 'int x = AV_CODEC_PROP_TEXT_SUB' && _avcodec_has_text_flag_api=yes +if test "$_avcodec_has_text_flag_api" = yes ; then + def_avcodec_has_text_flag_api='#define HAVE_AV_CODEC_PROP_TEXT_SUB 1' +else + def_avcodec_has_text_flag_api='#define HAVE_AV_CODEC_PROP_TEXT_SUB 0' +fi +echores "$_avcodec_has_text_flag_api" + + echocheck "libavutil QP API" _avutil_has_qp_api=no statement_check libavutil/frame.h 'av_frame_get_qp_table(NULL, NULL, NULL)' && _avutil_has_qp_api=yes @@ -3268,6 +3279,7 @@ $def_zlib $def_avutil_has_refcounting $def_avutil_has_qp_api +$def_avcodec_has_text_flag_api $def_libpostproc $def_libavdevice $def_libavfilter diff --git a/sub/sd_lavc_conv.c b/sub/sd_lavc_conv.c index e45e743499..744aa83440 100644 --- a/sub/sd_lavc_conv.c +++ b/sub/sd_lavc_conv.c @@ -22,6 +22,8 @@ #include #include +#include "config.h" + #include "talloc.h" #include "core/mp_msg.h" #include "core/av_common.h" @@ -35,8 +37,22 @@ static bool supports_format(const char *format) { enum AVCodecID cid = mp_codec_to_av_codec_id(format); const AVCodecDescriptor *desc = avcodec_descriptor_get(cid); + if (!desc) + return false; +#if HAVE_AV_CODEC_PROP_TEXT_SUB // These are documented to support AVSubtitleRect->ass. - return desc && (desc->props & AV_CODEC_PROP_TEXT_SUB); + return desc->props & AV_CODEC_PROP_TEXT_SUB; +#else + const char *whitelist[] = + {"text", "ass", "ssa", "mov_text", "srt", "subrip", "microdvd", "mpl2", + "jacosub", "pjs", "sami", "realtext", "subviewer", "subviewer1", + "vplayer", "webvtt", 0}; + for (int n = 0; whitelist[n]; n++) { + if (strcmp(format, whitelist[n]) == 0) + return true; + } + return false; +#endif } static int init(struct sd *sd) From d5520d20b26e7363882e9f1ebce7a5524a12321b Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 2 Jun 2013 19:57:37 +0200 Subject: [PATCH 18/28] sub: use libass even if -no-ass is used The -no-ass option used to disable all use of libass completely. This doesn't work this way anymore, and the text subtitle path has an inherent dependency on libass. Currently -no-ass does 3 things: 1. Strip tags and formatting on display, and use a separate renderer for the result. (Which might be the terminal, or libass via OSD code.) 2. Not loading attached fonts from Matroska files. 3. Use subreader.c instead of libass for reading .ass files. 1. and 2. are ok and what the user (probably wants), but 3. doesn't really make sense anymore. subreader.c reads .ass files just fine, but then does some strange things to them (something about coalescing and re-adding newlines?), leading to even more broken display with -no-ass. Instead of fighting with subreader.c, just use libass as loader. --- DOCS/man/en/options.rst | 10 ++++------ core/mplayer.c | 3 +-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/DOCS/man/en/options.rst b/DOCS/man/en/options.rst index a609e5f472..f626f861f6 100644 --- a/DOCS/man/en/options.rst +++ b/DOCS/man/en/options.rst @@ -90,13 +90,11 @@ - ``--aspect=16:9`` or ``--aspect=1.7777`` --ass, --no-ass - Render ASS subtitles natively, and convert text subtitles in other formats - to ASS internally (enabled by default). + Render ASS subtitles natively (enabled by default). - If ``--no-ass`` is specified, all subtitles are converted to plain text - internally. All tags and style declarations are stripped and ignored. The - subtitle renderer uses the font style as specified by the ``--sub-text-`` - options instead. + If ``--no-ass`` is specified, all tags and style declarations are stripped + and ignored on display. The subtitle renderer uses the font style as + specified by the ``--sub-text-`` options instead. *NOTE*: Using ``--no-ass`` may lead to incorrect or completely broken rendering of ASS/SSA subtitles. It can sometimes be useful to forcibly diff --git a/core/mplayer.c b/core/mplayer.c index de23e97a49..353723930c 100644 --- a/core/mplayer.c +++ b/core/mplayer.c @@ -1061,8 +1061,7 @@ struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename, // through sd_ass makes the code much simpler, as sd_ass can handle all // the weird special-cases. #ifdef CONFIG_ASS - if (opts->ass_enabled) - asst = mp_ass_read_stream(mpctx->ass_library, filename, opts->sub_cp); + asst = mp_ass_read_stream(mpctx->ass_library, filename, opts->sub_cp); if (!asst) subd = sub_read_file(filename, fps, &mpctx->opts); if (asst || subd) { From 5e2c211a4e1d67748348295b191acb2dfb76d024 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 2 Jun 2013 20:34:20 +0200 Subject: [PATCH 19/28] sd_lavc_conv: strip style header Normally, libavcodec subtitle converters will output a style header like this as part of the extradata: Style: Default,Arial,16,&Hffffff,&Hffffff,&H0,&H0,0,0,0,1,1,0,2,10,10,10,0,0 We don't want that, so use some bruteforce to get rid of them. --- sub/sd_lavc_conv.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/sub/sd_lavc_conv.c b/sub/sd_lavc_conv.c index 744aa83440..9c7a6f2c5f 100644 --- a/sub/sd_lavc_conv.c +++ b/sub/sd_lavc_conv.c @@ -27,6 +27,7 @@ #include "talloc.h" #include "core/mp_msg.h" #include "core/av_common.h" +#include "core/bstr.h" #include "sd.h" struct sd_lavc_priv { @@ -55,6 +56,19 @@ static bool supports_format(const char *format) #endif } +// Disable style definitions generated by the libavcodec converter. +// We always want the user defined style instead. +static void disable_styles(bstr header) +{ + while (header.len) { + int n = bstr_find(header, bstr0("\nStyle: ")); + if (n < 0) + break; + header.start[n + 1] = '#'; // turn into a comment + header = bstr_cut(header, 2); + } +} + static int init(struct sd *sd) { struct sd_lavc_priv *priv = talloc_zero(NULL, struct sd_lavc_priv); @@ -76,6 +90,11 @@ static int init(struct sd *sd) sd->output_codec = "ass"; sd->output_extradata = avctx->subtitle_header; sd->output_extradata_len = avctx->subtitle_header_size; + if (sd->output_extradata) { + sd->output_extradata = talloc_memdup(sd, sd->output_extradata, + sd->output_extradata_len); + disable_styles((bstr){sd->output_extradata, sd->output_extradata_len}); + } return 0; error: From 3913e3e383f2838c19c876ff1d809ac883014760 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 2 Jun 2013 22:35:00 +0200 Subject: [PATCH 20/28] sub: don't check for duplicates on sub conversion This mirrors commit "sub: remove check_duplicate_plaintext_event()". That code was basically duplicated. In general, this code is still needed when doing conversion during demuxing (mostly because you can seek during demuxing, which will cause duplicate events by replaying). --- sub/dec_sub.c | 7 ++++++- sub/sd.h | 4 ++++ sub/sd_ass.c | 12 +++++++----- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/sub/dec_sub.c b/sub/dec_sub.c index ec9af94abb..7e4427a40f 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -121,6 +121,10 @@ static void read_sub_data(struct dec_sub *sub, struct sub_data *subdata) assert(sub_accept_packets_in_advance(sub)); char *temp = NULL; + struct sd *sd = sub_get_last_sd(sub); + + sd->no_remove_duplicates = true; + for (int i = 0; i < subdata->sub_num; i++) { subtitle *st = &subdata->subtitles[i]; // subdata is in 10 ms ticks, pts is in seconds @@ -161,7 +165,6 @@ static void read_sub_data(struct dec_sub *sub, struct sub_data *subdata) sub_decode(sub, &pkt); } - struct sd *sd = sub_get_last_sd(sub); // Hack for broken FFmpeg packet format: make sd_ass keep the subtitle // events on reset(), even though broken FFmpeg ASS packets were received // (from sd_lavc_conv.c). Normally, these events are removed on seek/reset, @@ -169,6 +172,8 @@ static void read_sub_data(struct dec_sub *sub, struct sub_data *subdata) if (sd && sd->driver->fix_events) sd->driver->fix_events(sd); + sd->no_remove_duplicates = false; + talloc_free(temp); } diff --git a/sub/sd.h b/sub/sd.h index 20dce8003d..fc1085f8ed 100644 --- a/sub/sd.h +++ b/sub/sd.h @@ -27,6 +27,10 @@ struct sd { struct ass_library *ass_library; struct ass_renderer *ass_renderer; + // If false, try to remove multiple subtitles. + // (Only for decoders which have accept_packets_in_advance set.) + bool no_remove_duplicates; + // Set by sub converter const char *output_codec; char *output_extradata; diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 21d941933c..7e863a2844 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -132,11 +132,13 @@ static void decode(struct sd *sd, struct demux_packet *packet) return; } not_all_whitespace:; - for (int i = 0; i < track->n_events; i++) - if (track->events[i].Start == ipts - && (duration <= 0 || track->events[i].Duration == iduration) - && strcmp(track->events[i].Text, text) == 0) - return; // We've already added this subtitle + if (!sd->no_remove_duplicates) { + for (int i = 0; i < track->n_events; i++) + if (track->events[i].Start == ipts + && (duration <= 0 || track->events[i].Duration == iduration) + && strcmp(track->events[i].Text, text) == 0) + return; // We've already added this subtitle + } if (duration <= 0) { iduration = 10000; ctx->incomplete_event = true; From 5d517184f5c294e6aba8e2d729fad7caa5db75b4 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 2 Jun 2013 23:01:37 +0200 Subject: [PATCH 21/28] sub: never set VSFilter aspect if the ASS subtitle is converted When e.g. converting SRT to ASS, we certainly don't want them stretched by video aspect ratio, even if that's necessary for native ASS subtitles. Annoying weird details... --- sub/dec_sub.c | 1 + sub/sd.h | 4 ++++ sub/sd_ass.c | 3 ++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/sub/dec_sub.c b/sub/dec_sub.c index 7e4427a40f..4eb0dfa99f 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -224,6 +224,7 @@ void sub_init_from_sh(struct dec_sub *sub, struct sh_sub *sh) } init_sd = (struct sd) { .codec = sd->output_codec, + .converted_from = sd->codec, .extradata = sd->output_extradata, .extradata_len = sd->output_extradata_len, .ass_library = sub->init_sd.ass_library, diff --git a/sub/sd.h b/sub/sd.h index fc1085f8ed..a2f57a5808 100644 --- a/sub/sd.h +++ b/sub/sd.h @@ -16,6 +16,10 @@ struct sd { char *extradata; int extradata_len; + // Set to !=NULL if the input packets are being converted from another + // format. + const char *converted_from; + // 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; diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 7e863a2844..1f7f33b026 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -67,6 +67,7 @@ static int init(struct sd *sd) return -1; bool ass = is_native_ass(sd->codec); + bool is_converted = sd->converted_from != NULL; struct sd_ass_priv *ctx = talloc_zero(NULL, struct sd_ass_priv); sd->priv = ctx; if (sd->ass_track) { @@ -81,7 +82,7 @@ static int init(struct sd *sd) sd->extradata_len); } - ctx->vsfilter_aspect = ass; + ctx->vsfilter_aspect = !is_converted; return 0; } From 13a1ce16f9581871cf7ac0d06ece407534a98f89 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 3 Jun 2013 01:28:14 +0200 Subject: [PATCH 22/28] sub: pass subtitle packets directly Before this, subtitle packets were returned as data ptr/len pairs, and mplayer.c got the rest (pts and duration) directly from the demuxer data structures. Then mplayer.c reassembled the packet data structure again. Pass packets directly instead. The mplayer.c side stays a bit awkward, because the (now by default unused) DVD path keeps getting in the way. In demux.c there's lots of weird stuff (3 functions that read packets, really?), but we want to keep the code equivalent for now to avoid hitting weird issues and corner cases. --- core/mplayer.c | 19 ++++++++----------- demux/demux.c | 18 +++++++++--------- demux/demux.h | 2 +- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/core/mplayer.c b/core/mplayer.c index 353723930c..80ddd43f95 100644 --- a/core/mplayer.c +++ b/core/mplayer.c @@ -1885,18 +1885,15 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl) if (non_interleaved && subpts_s > curpts_s + 1) break; } - double duration = d_sub->first->duration; - unsigned char *packet = NULL; - int len = ds_get_packet_sub(d_sub, &packet); + struct demux_packet pkt; + struct demux_packet *orig = ds_get_packet_sub(d_sub); + if (!orig) + break; + pkt = *orig; + pkt.pts = subpts_s; mp_dbg(MSGT_CPLAYER, MSGL_V, "Sub: c_pts=%5.3f s_pts=%5.3f " - "duration=%5.3f len=%d\n", curpts_s, subpts_s, duration, - len); - struct demux_packet pkt = { - .buffer = packet, - .len = len, - .pts = subpts_s, - .duration = duration, - }; + "duration=%5.3f len=%d\n", curpts_s, pkt.pts, pkt.duration, + pkt.len); sub_decode(dec_sub, &pkt); } } diff --git a/demux/demux.c b/demux/demux.c index 12e0d9083a..25f31a3f60 100644 --- a/demux/demux.c +++ b/demux/demux.c @@ -801,20 +801,20 @@ int ds_get_packet_pts(demux_stream_t *ds, unsigned char **start, double *pts) return len; } -int ds_get_packet_sub(demux_stream_t *ds, unsigned char **start) +struct demux_packet *ds_get_packet_sub(demux_stream_t *ds) { - int len; if (ds->buffer_pos >= ds->buffer_size) { - *start = NULL; if (!ds->packs) - return -1; // no sub + return NULL; // no sub if (!ds_fill_buffer(ds)) - return -1; // EOF + return NULL; // EOF } - len = ds->buffer_size - ds->buffer_pos; - *start = &ds->buffer[ds->buffer_pos]; - ds->buffer_pos += len; - return len; + if (ds->buffer_pos < ds->buffer_size) { + ds->current->buffer += ds->buffer_pos; + ds->buffer_size -= ds->buffer_pos; + } + ds->buffer_pos = ds->buffer_size; + return ds->current; } struct demux_packet *ds_get_packet2(struct demux_stream *ds, bool repeat_last) diff --git a/demux/demux.h b/demux/demux.h index e58c56141f..9ec6d0c6f0 100644 --- a/demux/demux.h +++ b/demux/demux.h @@ -344,7 +344,7 @@ void ds_free_packs(struct demux_stream *ds); int ds_get_packet(struct demux_stream *ds, unsigned char **start); int ds_get_packet_pts(struct demux_stream *ds, unsigned char **start, double *pts); -int ds_get_packet_sub(struct demux_stream *ds, unsigned char **start); +struct demux_packet *ds_get_packet_sub(demux_stream_t *ds); struct demux_packet *ds_get_packet2(struct demux_stream *ds, bool repeat_last); double ds_get_next_pts(struct demux_stream *ds); int ds_parse(struct demux_stream *sh, uint8_t **buffer, int *len, double pts, From 9f4261de65c18d3a34e70c9f969966ca85c80a8d Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 3 Jun 2013 01:55:48 +0200 Subject: [PATCH 23/28] core: add common function to initialize AVPacket Audio and video had their own (very similar) functions to initialize an AVPacket (ffmpeg's packet struct) from a demux_packet (mplayer's packet struct). Add a common function for these. Also use this function for sd_lavc_conv. This is actually a functional change, as some libavfilter subtitle demuxers add weird out-of-band stuff as side-data. --- audio/decode/ad_lavc.c | 7 ++----- core/av_common.c | 20 ++++++++++++++++++++ core/av_common.h | 2 ++ sub/sd_lavc_conv.c | 5 +---- video/decode/vd_lavc.c | 13 ++----------- 5 files changed, 27 insertions(+), 20 deletions(-) diff --git a/audio/decode/ad_lavc.c b/audio/decode/ad_lavc.c index 8abc0a6035..b5a4ee1ef8 100644 --- a/audio/decode/ad_lavc.c +++ b/audio/decode/ad_lavc.c @@ -402,13 +402,10 @@ static int decode_new_packet(struct sh_audio *sh) } AVPacket pkt; - av_init_packet(&pkt); + mp_set_av_packet(&pkt, mpkt); pkt.data = start; pkt.size = insize; - if (mpkt && mpkt->avpacket) { - pkt.side_data = mpkt->avpacket->side_data; - pkt.side_data_elems = mpkt->avpacket->side_data_elems; - } + if (pts != MP_NOPTS_VALUE && !packet_already_used) { sh->pts = pts; sh->pts_bytes = 0; diff --git a/core/av_common.c b/core/av_common.c index 5e6c8a4352..a4dc525aa9 100644 --- a/core/av_common.c +++ b/core/av_common.c @@ -18,8 +18,10 @@ #include #include +#include #include "core/mp_talloc.h" +#include "demux/demux_packet.h" #include "av_common.h" #include "codecs.h" @@ -58,6 +60,24 @@ void mp_copy_lav_codec_headers(AVCodecContext *avctx, AVCodecContext *st) avctx->bits_per_coded_sample = st->bits_per_coded_sample; } +// Set dst from mpkt. Note that dst is not refcountable. +// mpkt can be NULL to generate empty packets (used to flush delayed data). +// Does not set pts or duration fields. +void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt) +{ + av_init_packet(dst); + dst->data = mpkt ? mpkt->buffer : NULL; + dst->size = mpkt ? mpkt->len : 0; + /* Some codecs (ZeroCodec, some cases of PNG) may want keyframe info + * from demuxer. */ + if (mpkt && mpkt->keyframe) + dst->flags |= AV_PKT_FLAG_KEY; + if (mpkt && mpkt->avpacket) { + dst->side_data = mpkt->avpacket->side_data; + dst->side_data_elems = mpkt->avpacket->side_data_elems; + } +} + void mp_add_lavc_decoders(struct mp_decoder_list *list, enum AVMediaType type) { AVCodec *cur = NULL; diff --git a/core/av_common.h b/core/av_common.h index 25593ed3d0..2fa8f127b0 100644 --- a/core/av_common.h +++ b/core/av_common.h @@ -22,8 +22,10 @@ #include struct mp_decoder_list; +struct demux_packet; void mp_copy_lav_codec_headers(AVCodecContext *avctx, AVCodecContext *st); +void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt); void mp_add_lavc_decoders(struct mp_decoder_list *list, enum AVMediaType type); int mp_codec_to_av_codec_id(const char *codec); const char *mp_codec_from_av_codec_id(int codec_id); diff --git a/sub/sd_lavc_conv.c b/sub/sd_lavc_conv.c index 9c7a6f2c5f..5653ad87f9 100644 --- a/sub/sd_lavc_conv.c +++ b/sub/sd_lavc_conv.c @@ -114,9 +114,7 @@ static void decode(struct sd *sd, struct demux_packet *packet) AVPacket pkt; int ret, got_sub; - av_init_packet(&pkt); - pkt.data = packet->buffer; - pkt.size = packet->len; + mp_set_av_packet(&pkt, packet); pkt.pts = packet->pts == MP_NOPTS_VALUE ? AV_NOPTS_VALUE : packet->pts * ts; pkt.duration = packet->duration * ts; @@ -136,7 +134,6 @@ static void decode(struct sd *sd, struct demux_packet *packet) } } - av_free_packet(&pkt); avsubtitle_free(&sub); } diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c index fff44edaed..540204bcfd 100644 --- a/video/decode/vd_lavc.c +++ b/video/decode/vd_lavc.c @@ -673,17 +673,8 @@ static int decode(struct sh_video *sh, struct demux_packet *packet, else avctx->skip_frame = ctx->skip_frame; - av_init_packet(&pkt); - pkt.data = packet ? packet->buffer : NULL; - pkt.size = packet ? packet->len : 0; - /* Some codecs (ZeroCodec, some cases of PNG) may want keyframe info - * from demuxer. */ - if (packet && packet->keyframe) - pkt.flags |= AV_PKT_FLAG_KEY; - if (packet && packet->avpacket) { - pkt.side_data = packet->avpacket->side_data; - pkt.side_data_elems = packet->avpacket->side_data_elems; - } + mp_set_av_packet(&pkt, packet); + // The avcodec opaque field stupidly supports only int64_t type union pts { int64_t i; double d; }; avctx->reordered_opaque = (union pts){.d = *reordered_pts}.i; From 8c63b318dc106f43d9ab17250452216bab485587 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 3 Jun 2013 22:14:56 +0200 Subject: [PATCH 24/28] ass_mp: provide function to add default styles --- sub/ass_mp.c | 37 ++++++++++++++++++++++++------------- sub/ass_mp.h | 4 +++- sub/osd_libass.c | 6 +++--- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/sub/ass_mp.c b/sub/ass_mp.c index 85105a33ad..258dd57688 100644 --- a/sub/ass_mp.c +++ b/sub/ass_mp.c @@ -38,7 +38,9 @@ #include "stream/stream.h" #include "core/options.h" -void mp_ass_set_style(ASS_Style *style, struct osd_style_opts *opts) +// res_y should be track->PlayResY +// It determines scaling of font sizes and more. +void mp_ass_set_style(ASS_Style *style, int res_y, struct osd_style_opts *opts) { if (opts->font) { free(style->FontName); @@ -46,9 +48,9 @@ void mp_ass_set_style(ASS_Style *style, struct osd_style_opts *opts) style->treat_fontname_as_pattern = 1; } - // libass_font_size = FontSize * (window_height / MP_ASS_FONT_PLAYRESY) - // scale translates parameters from PlayResY=720 to MP_ASS_FONT_PLAYRESY - double scale = MP_ASS_FONT_PLAYRESY / 720.0; + // libass_font_size = FontSize * (window_height / res_y) + // scale translates parameters from PlayResY=720 to res_y + double scale = res_y / 720.0; style->FontSize = opts->font_size * scale; style->PrimaryColour = MP_ASS_COLOR(opts->color); @@ -74,30 +76,39 @@ void mp_ass_set_style(ASS_Style *style, struct osd_style_opts *opts) #endif } -ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts) +// Add default styles, if the track does not have any styles yet. +// Apply style overrides if the user provides any. +void mp_ass_add_default_styles(ASS_Track *track, struct MPOpts *opts) { - ASS_Track *track = ass_new_track(library); - - track->track_type = TRACK_TYPE_ASS; - track->Timer = 100.; - track->PlayResY = MP_ASS_FONT_PLAYRESY; - track->WrapStyle = 0; - if (opts->ass_styles_file && opts->ass_style_override) ass_read_styles(track, opts->ass_styles_file, opts->sub_cp); if (track->n_styles == 0) { + if (!track->PlayResY) { + track->PlayResY = MP_ASS_FONT_PLAYRESY; + track->PlayResX = track->PlayResY * 4 / 3; + } track->Kerning = true; int sid = ass_alloc_style(track); track->default_style = sid; ASS_Style *style = track->styles + sid; style->Name = strdup("Default"); style->Alignment = 2; - mp_ass_set_style(style, opts->sub_text_style); + mp_ass_set_style(style, track->PlayResY, opts->sub_text_style); } if (opts->ass_style_override) ass_process_force_style(track); +} + +ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts) +{ + ASS_Track *track = ass_new_track(library); + + track->track_type = TRACK_TYPE_ASS; + track->Timer = 100.; + + mp_ass_add_default_styles(track, opts); return track; } diff --git a/sub/ass_mp.h b/sub/ass_mp.h index 4b31a832a4..b6ec2808c0 100644 --- a/sub/ass_mp.h +++ b/sub/ass_mp.h @@ -44,7 +44,9 @@ struct MPOpts; struct mp_osd_res; struct osd_style_opts; -void mp_ass_set_style(ASS_Style *style, struct osd_style_opts *opts); +void mp_ass_set_style(ASS_Style *style, int res_y, struct osd_style_opts *opts); + +void mp_ass_add_default_styles(ASS_Track *track, struct MPOpts *opts); ASS_Track *mp_ass_default_track(ASS_Library *library, struct MPOpts *opts); ASS_Track *mp_ass_read_stream(ASS_Library *library, const char *fname, diff --git a/sub/osd_libass.c b/sub/osd_libass.c index 6733b9b6c2..cbf9466780 100644 --- a/sub/osd_libass.c +++ b/sub/osd_libass.c @@ -89,7 +89,7 @@ static void create_osd_ass_track(struct osd_state *osd, struct osd_object *obj) ASS_Style *style = track->styles + sid; style->Alignment = 5; // top-title, left style->Name = strdup("OSD"); - mp_ass_set_style(style, osd->opts->osd_style); + mp_ass_set_style(style, MP_ASS_FONT_PLAYRESY, osd->opts->osd_style); // Set to neutral base direction, as opposed to VSFilter LTR default style->Encoding = -1; } @@ -158,7 +158,7 @@ static void update_osd(struct osd_state *osd, struct osd_object *obj) font.font_size *= opts->osd_scale; ASS_Style *style = obj->osd_track->styles + obj->osd_track->default_style; - mp_ass_set_style(style, &font); + mp_ass_set_style(style, obj->osd_track->PlayResY, &font); char *text = mangle_ass(osd->osd_text); add_osd_ass_event(obj->osd_track, text); @@ -364,7 +364,7 @@ static void update_sub(struct osd_state *osd, struct osd_object *obj) font.font_size *= opts->sub_scale; ASS_Style *style = obj->osd_track->styles + obj->osd_track->default_style; - mp_ass_set_style(style, &font); + mp_ass_set_style(style, obj->osd_track->PlayResY, &font); #if LIBASS_VERSION >= 0x01010000 ass_set_line_position(osd->osd_render, 100 - opts->sub_pos); From 3289be9a2856c0f935fcb49768fc39d878044202 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 3 Jun 2013 02:05:55 +0200 Subject: [PATCH 25/28] sd_ass: add default style if there aren't any styles The default style is added by mp_ass_default_track(), but not by ass_new_track(). Considering this, the previous condition at this point didn't make much sense anymore: the actual (converted) subtitle format doesn't matter much for what styling should be applied. What matters is if the subtitle was originally ASS, or if it was converted to it. Change the code such that the default style is added if there aren't any, even after reading sub extradata. (The extradata contains the ASS header, including the style section.) This might change behavior with scripts that don't define any styles. The change is either with this commit or with an earlier commit in this branch, depending on the situation - there are multiple places where default styles are added in libass API functions, and it's all a big mess. Other than with very old or broken files (where different behavior doesn't matter much), the current code should be pretty safe, though. --- sub/sd_ass.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 1f7f33b026..c4373d4511 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -63,25 +63,29 @@ static void free_last_event(ASS_Track *track) static int init(struct sd *sd) { + struct MPOpts *opts = sd->opts; if (!sd->ass_library || !sd->ass_renderer) return -1; - bool ass = is_native_ass(sd->codec); bool is_converted = sd->converted_from != NULL; + struct sd_ass_priv *ctx = talloc_zero(NULL, struct sd_ass_priv); sd->priv = ctx; if (sd->ass_track) { ctx->ass_track = sd->ass_track; - } else if (ass) { + } else { ctx->ass_track = ass_new_track(sd->ass_library); - } else - ctx->ass_track = mp_ass_default_track(sd->ass_library, sd->opts); + if (!is_converted) + ctx->ass_track->track_type = TRACK_TYPE_ASS; + } if (sd->extradata) { ass_process_codec_private(ctx->ass_track, sd->extradata, sd->extradata_len); } + mp_ass_add_default_styles(ctx->ass_track, opts); + ctx->vsfilter_aspect = !is_converted; return 0; } From 61dfe121791adf953386642844cd9258ba99f179 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 3 Jun 2013 21:49:39 +0200 Subject: [PATCH 26/28] sub: add name field to all sub decoders Might help with debugging. --- sub/dec_sub.c | 12 ++++++++++++ sub/sd.h | 1 + sub/sd_ass.c | 1 + sub/sd_lavc.c | 1 + sub/sd_lavc_conv.c | 1 + sub/sd_microdvd.c | 1 + sub/sd_movtext.c | 1 + sub/sd_spu.c | 1 + sub/sd_srt.c | 1 + 9 files changed, 20 insertions(+) diff --git a/sub/dec_sub.c b/sub/dec_sub.c index 4eb0dfa99f..b72630470c 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -115,6 +115,17 @@ void sub_set_ass_renderer(struct dec_sub *sub, struct ass_library *ass_library, sub->init_sd.ass_renderer = ass_renderer; } +static void print_chain(struct dec_sub *sub) +{ + mp_msg(MSGT_OSD, MSGL_V, "Subtitle filter chain: "); + for (int n = 0; n < sub->num_sd; n++) { + struct sd *sd = sub->sd[n]; + mp_msg(MSGT_OSD, MSGL_V, "%s%s (%s)", n > 0 ? " -> " : "", + sd->driver->name, sd->codec); + } + mp_msg(MSGT_OSD, MSGL_V, "\n"); +} + // Subtitles read with subreader.c static void read_sub_data(struct dec_sub *sub, struct sub_data *subdata) { @@ -218,6 +229,7 @@ void sub_init_from_sh(struct dec_sub *sub, struct sh_sub *sh) sub->num_sd++; // Try adding new converters until a decoder is reached if (sd->driver->get_bitmaps || sd->driver->get_text) { + print_chain(sub); if (sh->sub_data) read_sub_data(sub, sh->sub_data); return; diff --git a/sub/sd.h b/sub/sd.h index a2f57a5808..4268137921 100644 --- a/sub/sd.h +++ b/sub/sd.h @@ -45,6 +45,7 @@ struct sd { }; struct sd_functions { + const char *name; bool accept_packets_in_advance; bool (*supports_format)(const char *format); int (*init)(struct sd *sd); diff --git a/sub/sd_ass.c b/sub/sd_ass.c index c4373d4511..c46c55c1ab 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -300,6 +300,7 @@ static void uninit(struct sd *sd) } const struct sd_functions sd_ass = { + .name = "ass", .accept_packets_in_advance = true, .supports_format = supports_format, .init = init, diff --git a/sub/sd_lavc.c b/sub/sd_lavc.c index 9f8db2d877..cb90e78a56 100644 --- a/sub/sd_lavc.c +++ b/sub/sd_lavc.c @@ -244,6 +244,7 @@ static void uninit(struct sd *sd) } const struct sd_functions sd_lavc = { + .name = "lavc", .supports_format = supports_format, .init = init, .decode = decode, diff --git a/sub/sd_lavc_conv.c b/sub/sd_lavc_conv.c index 5653ad87f9..1fc0262f96 100644 --- a/sub/sd_lavc_conv.c +++ b/sub/sd_lavc_conv.c @@ -155,6 +155,7 @@ static void uninit(struct sd *sd) } const struct sd_functions sd_lavc_conv = { + .name = "lavc_conv", .supports_format = supports_format, .init = init, .decode = decode, diff --git a/sub/sd_microdvd.c b/sub/sd_microdvd.c index eba5b67576..adf8679676 100644 --- a/sub/sd_microdvd.c +++ b/sub/sd_microdvd.c @@ -329,6 +329,7 @@ static void decode(struct sd *sd, struct demux_packet *packet) } const struct sd_functions sd_microdvd = { + .name = "microdvd", .supports_format = supports_format, .init = init, .decode = decode, diff --git a/sub/sd_movtext.c b/sub/sd_movtext.c index 30d2da8254..a6ef120ec7 100644 --- a/sub/sd_movtext.c +++ b/sub/sd_movtext.c @@ -46,6 +46,7 @@ static void decode(struct sd *sd, struct demux_packet *packet) } const struct sd_functions sd_movtext = { + .name = "movtext", .supports_format = supports_format, .init = init, .decode = decode, diff --git a/sub/sd_spu.c b/sub/sd_spu.c index d2dd5f56e0..c6aed81641 100644 --- a/sub/sd_spu.c +++ b/sub/sd_spu.c @@ -92,6 +92,7 @@ static void uninit(struct sd *sd) } const struct sd_functions sd_spu = { + .name = "spu", .supports_format = supports_format, .init = init, .decode = decode, diff --git a/sub/sd_srt.c b/sub/sd_srt.c index ec4768a598..fcad088dc1 100644 --- a/sub/sd_srt.c +++ b/sub/sd_srt.c @@ -458,6 +458,7 @@ static void decode(struct sd *sd, struct demux_packet *packet) } const struct sd_functions sd_srt = { + .name = "srt", .supports_format = supports_format, .init = init, .decode = decode, From f3871193fcfc5feaee42c883496fa5f8f99eb4f5 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 3 Jun 2013 23:00:39 +0200 Subject: [PATCH 27/28] sd_srt, sd_microdvd: set ASS script resolution Both converters can output \pos and deal with font sizes, so they assume a specific script resolution (PlayResX/PlayResY). The implicit assumption was that a specific resolution was guaranteed. The MP_ASS_FONT_PLAYRESY constant is connected to this. Better make it explicit, so that the implicit dependency on MP_ASS_FONT_PLAYRESY is removed. (Unfortunately, libavcodec sub converters still don't set PlayResX/PlayResY explicitly, so the value set by that constant can't be declared as arbitrary yet.) PlayResY=288 is most likely the SSA natural script resolution (or something like this?), as well as the libass and VSFilter default. PlayResX=384 is the fallback value set by libass if PlayResY is set to 288, and PlayResX is unset. --- sub/ass_mp.h | 3 ++- sub/sd_microdvd.c | 8 ++++++++ sub/sd_srt.c | 8 ++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/sub/ass_mp.h b/sub/ass_mp.h index b6ec2808c0..9f40b34166 100644 --- a/sub/ass_mp.h +++ b/sub/ass_mp.h @@ -27,7 +27,8 @@ #include "config.h" #include "subreader.h" -// font sizes and explicit tags in subassconvert.c assume this size (?) +// This is probably arbitrary. +// sd_lavc_conv might indirectly still assume this PlayResY, though. #define MP_ASS_FONT_PLAYRESY 288 #define MP_ASS_RGBA(r, g, b, a) \ diff --git a/sub/sd_microdvd.c b/sub/sd_microdvd.c index adf8679676..81c8ef92c8 100644 --- a/sub/sd_microdvd.c +++ b/sub/sd_microdvd.c @@ -309,6 +309,12 @@ static void convert_microdvd(const char *orig, char *dest, int dest_buffer_size) new_line.buf[new_line.len] = 0; } +static const char *microdvd_ass_extradata = + "[Script Info]\n" + "ScriptType: v4.00+\n" + "PlayResX: 384\n" + "PlayResY: 288\n"; + static bool supports_format(const char *format) { return format && strcmp(format, "microdvd") == 0; @@ -317,6 +323,8 @@ static bool supports_format(const char *format) static int init(struct sd *sd) { sd->output_codec = "ass-text"; + sd->output_extradata = (char *)microdvd_ass_extradata; + sd->output_extradata_len = strlen(sd->output_extradata); return 0; } diff --git a/sub/sd_srt.c b/sub/sd_srt.c index fcad088dc1..6258acde5b 100644 --- a/sub/sd_srt.c +++ b/sub/sd_srt.c @@ -437,6 +437,12 @@ static void convert_subrip(const char *orig, char *dest, int dest_buffer_size) new_line.buf[new_line.len] = 0; } +static const char *srt_ass_extradata = + "[Script Info]\n" + "ScriptType: v4.00+\n" + "PlayResX: 384\n" + "PlayResY: 288\n"; + static bool supports_format(const char *format) { return format && (strcmp(format, "subrip") == 0 || @@ -446,6 +452,8 @@ static bool supports_format(const char *format) static int init(struct sd *sd) { sd->output_codec = "ass-text"; + sd->output_extradata = (char *)srt_ass_extradata; + sd->output_extradata_len = strlen(sd->output_extradata); return 0; } From c1ac97b99b3e80bbf84ed540178dd6689ead0b87 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 4 Jun 2013 00:17:51 +0200 Subject: [PATCH 28/28] sub: always show subtitles on terminal with -no-video Until now, this happened only when the -no-ass option was used. This difference in behavior doesn't make much sense, so change it so that whether -no-ass is used or not doesn't matter. (-no-ass enables the OSD subtitle renderer, which has the terminal fallback, while the normal path is video only.) the changes in set_osd_subtitle() and reinit_video_chain() are for resetting the state correctly when switching between video/no-video. --- core/mplayer.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/mplayer.c b/core/mplayer.c index 80ddd43f95..4d71b5f55f 100644 --- a/core/mplayer.c +++ b/core/mplayer.c @@ -1515,6 +1515,8 @@ static void set_osd_subtitle(struct MPContext *mpctx, const char *text) 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 @@ -1898,7 +1900,7 @@ static void update_subtitles(struct MPContext *mpctx, double refpts_tl) } } - if (!mpctx->osd->render_bitmap_subs) + if (!mpctx->osd->render_bitmap_subs || !mpctx->sh_video) set_osd_subtitle(mpctx, sub_get_text(dec_sub, curpts_s)); } @@ -2481,7 +2483,7 @@ int reinit_video_chain(struct MPContext *mpctx) mpctx->delay = 0; mpctx->vo_pts_history_seek_ts++; - // ========== Init display (sh_video->disp_w*sh_video->disp_h/out_fmt) ============ + reset_subtitles(mpctx); return 1;