From 26842806431a1d21e3c3c430994cd6901e36a08e Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 29 Apr 2013 01:13:22 +0200 Subject: 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. --- demux/demux_mpg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'demux') 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); } -- cgit v1.2.3 From 27d383918a3d63559c85ca96b2162a13234f2abc Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 1 Jun 2013 19:43:11 +0200 Subject: 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 (limited to 'demux') 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); -#endif + 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); - 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; + 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 - 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; + // Used with libavformat 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; } 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; -- cgit v1.2.3 From 02ce316ade9ba932ad405383278d6b01c54e5fc4 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 1 Jun 2013 19:44:12 +0200 Subject: 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 | 163 ++++++++++++++++++++++++++++++++++--------------------- sub/dec_sub.h | 36 ++++++++---- sub/sd.h | 40 +++++++++++--- sub/sd_ass.c | 80 +++++++++++++-------------- 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(-) (limited to 'demux') 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 (!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; - 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); + 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) +{ + 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) { - sh->sd_driver = NULL; + 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; - 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; + return sub->sd && sub->sd->driver->get_text; } -void sub_reset(struct sh_sub *sh, struct osd_state *osd) +char *sub_get_text(struct dec_sub *sub, double pts) { - 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; + struct MPOpts *opts = sub->opts; + char *text = NULL; + if (sub->sd && opts->sub_visibility) { + if (sub->sd->driver->get_text) + text = sub->sd->driver->get_text(sub->sd, pts); } - sh->active = false; + return text; } -void sub_uninit(struct sh_sub *sh) +void sub_reset(struct dec_sub *sub) { - 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 (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); - } + if (!sd->ass_library || !sd->ass_renderer) + return -1; + + 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; -- cgit v1.2.3 From e19ffa02aa370cbc3b559f85b286ea09b06ab29b Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 1 Jun 2013 19:54:18 +0200 Subject: 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 (limited to 'demux') 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; -- cgit v1.2.3 From 6dbedd27d53b61744d512e27bb0c3853cf6d7bba Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 1 Jun 2013 19:54:34 +0200 Subject: 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(-) (limited to 'demux') 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; -- cgit v1.2.3 From 13a1ce16f9581871cf7ac0d06ece407534a98f89 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 3 Jun 2013 01:28:14 +0200 Subject: 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(-) (limited to 'demux') 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, -- cgit v1.2.3