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;