diff options
Diffstat (limited to 'sub/dec_sub.c')
-rw-r--r-- | sub/dec_sub.c | 370 |
1 files changed, 308 insertions, 62 deletions
diff --git a/sub/dec_sub.c b/sub/dec_sub.c index d3cedea80d..b72630470c 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -22,105 +22,351 @@ #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" 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; +extern const struct sd_functions sd_lavc_conv; -bool is_text_sub(const char *t) +static const struct sd_functions *sd_list[] = { +#ifdef CONFIG_ASS + &sd_ass, +#endif + &sd_lavc, + &sd_spu, + &sd_movtext, + &sd_srt, + &sd_microdvd, + &sd_lavc_conv, + NULL +}; + +#define MAX_NUM_SD 3 + +struct dec_sub { + struct MPOpts *opts; + struct sd init_sd; + + struct sd *sd[MAX_NUM_SD]; + int num_sd; +}; + +struct dec_sub *sub_create(struct MPOpts *opts) { - return t && (is_ass_sub(t) || - strcmp(t, "text") == 0 || - strcmp(t, "subrip") == 0 || - strcmp(t, "mov_text") == 0); + struct dec_sub *sub = talloc_zero(NULL, struct dec_sub); + sub->opts = opts; + return sub; } -bool is_ass_sub(const char *t) +static void sub_uninit(struct dec_sub *sub) { - return t && (strcmp(t, "ass") == 0 || - strcmp(t, "ssa") == 0); + sub_reset(sub); + for (int n = 0; n < sub->num_sd; n++) { + if (sub->sd[n]->driver->uninit) + sub->sd[n]->driver->uninit(sub->sd[n]); + talloc_free(sub->sd[n]); + } + sub->num_sd = 0; } -bool is_dvd_sub(const char *t) +void sub_destroy(struct dec_sub *sub) { - return t && strcmp(t, "dvd_subtitle") == 0; + if (!sub) + return; + sub_uninit(sub); + talloc_free(sub); } -void sub_init(struct sh_sub *sh, struct osd_state *osd) +bool sub_is_initialized(struct dec_sub *sub) { - struct MPOpts *opts = sh->opts; + return !!sub->num_sd; +} - assert(!osd->sh_sub); - if (sd_lavc.probe(sh)) - sh->sd_driver = &sd_lavc; -#ifdef CONFIG_ASS - if (opts->ass_enabled && sd_ass.probe(sh)) - sh->sd_driver = &sd_ass; -#endif - if (sh->sd_driver) { - if (sh->sd_driver->init(sh, osd) < 0) +struct sd *sub_get_last_sd(struct dec_sub *sub) +{ + return sub->num_sd ? sub->sd[sub->num_sd - 1] : NULL; +} + +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 void print_chain(struct dec_sub *sub) +{ + mp_msg(MSGT_OSD, MSGL_V, "Subtitle filter chain: "); + for (int n = 0; n < sub->num_sd; n++) { + struct sd *sd = sub->sd[n]; + mp_msg(MSGT_OSD, MSGL_V, "%s%s (%s)", n > 0 ? " -> " : "", + sd->driver->name, sd->codec); + } + mp_msg(MSGT_OSD, MSGL_V, "\n"); +} + +// Subtitles read with subreader.c +static void read_sub_data(struct dec_sub *sub, struct sub_data *subdata) +{ + assert(sub_accept_packets_in_advance(sub)); + char *temp = NULL; + + struct sd *sd = sub_get_last_sd(sub); + + sd->no_remove_duplicates = true; + + for (int i = 0; i < subdata->sub_num; i++) { + subtitle *st = &subdata->subtitles[i]; + // subdata is in 10 ms ticks, pts is in seconds + 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); + } + + // Hack for broken FFmpeg packet format: make sd_ass keep the subtitle + // events on reset(), even though broken FFmpeg ASS packets were received + // (from sd_lavc_conv.c). Normally, these events are removed on seek/reset, + // but this is obviously unwanted in this case. + if (sd && sd->driver->fix_events) + sd->driver->fix_events(sd); + + sd->no_remove_duplicates = false; + + talloc_free(temp); +} + +static int sub_init_decoder(struct dec_sub *sub, struct sd *sd) +{ + sd->driver = NULL; + for (int n = 0; sd_list[n]; n++) { + if (sd_list[n]->supports_format(sd->codec)) { + sd->driver = sd_list[n]; + break; + } + } + + 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->num_sd); + + if (sh->extradata && !sub->init_sd.extradata) + sub_set_extradata(sub, sh->extradata, sh->extradata_len); + struct sd init_sd = sub->init_sd; + init_sd.codec = sh->gsh->codec; + init_sd.ass_track = sh->track; + + while (sub->num_sd < MAX_NUM_SD) { + struct sd *sd = talloc(NULL, struct sd); + *sd = init_sd; + sd->opts = sub->opts; + if (sub_init_decoder(sub, sd) < 0) { + talloc_free(sd); + break; + } + sub->sd[sub->num_sd] = sd; + sub->num_sd++; + // Try adding new converters until a decoder is reached + if (sd->driver->get_bitmaps || sd->driver->get_text) { + print_chain(sub); + if (sh->sub_data) + read_sub_data(sub, sh->sub_data); return; - osd->sh_sub = sh; - osd->switch_sub_id++; - sh->initialized = true; - sh->active = true; + } + init_sd = (struct sd) { + .codec = sd->output_codec, + .converted_from = sd->codec, + .extradata = sd->output_extradata, + .extradata_len = sd->output_extradata_len, + .ass_library = sub->init_sd.ass_library, + .ass_renderer = sub->init_sd.ass_renderer, + }; + } + + sub_uninit(sub); + mp_msg(MSGT_OSD, MSGL_ERR, "Could not find subtitle decoder for format '%s'.\n", + sh->gsh->codec ? sh->gsh->codec : "<unknown>"); +} + +bool sub_accept_packets_in_advance(struct dec_sub *sub) +{ + // Converters are assumed to always accept packets in advance + struct sd *sd = sub_get_last_sd(sub); + return sd && sd->driver->accept_packets_in_advance; +} + +static void decode_next(struct dec_sub *sub, int n, struct demux_packet *packet) +{ + struct sd *sd = sub->sd[n]; + sd->driver->decode(sd, packet); + if (n + 1 >= sub->num_sd || !sd->driver->get_converted) + return; + while (1) { + struct demux_packet *next = + sd->driver->get_converted ? sd->driver->get_converted(sd) : NULL; + if (!next) + break; + decode_next(sub, n + 1, next); } } -void sub_decode(struct 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->num_sd > 0) + decode_next(sub, 0, 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; + struct sd *sd = sub_get_last_sd(sub); *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 (sd && opts->sub_visibility) { + if (sd->driver->get_bitmaps) + sd->driver->get_bitmaps(sd, dim, pts, res); } +} - res->bitmap_id += osd->switch_sub_id; - res->bitmap_pos_id += osd->switch_sub_id; - osd->switch_sub_id = 0; +bool sub_has_get_text(struct dec_sub *sub) +{ + struct sd *sd = sub_get_last_sd(sub); + return sd && 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); + struct MPOpts *opts = sub->opts; + struct sd *sd = sub_get_last_sd(sub); + char *text = NULL; + if (sd && opts->sub_visibility) { + if (sd->driver->get_text) + text = sd->driver->get_text(sd, pts); + } + return text; } -void sub_switchoff(struct sh_sub *sh, struct osd_state *osd) +void sub_reset(struct dec_sub *sub) { - if (sh->active && sh->sd_driver->switch_off) { - assert(osd->sh_sub == sh); - sh->sd_driver->switch_off(sh, osd); - osd->sh_sub = NULL; + for (int n = 0; n < sub->num_sd; n++) { + if (sub->sd[n]->driver->reset) + sub->sd[n]->driver->reset(sub->sd[n]); } - sh->active = false; } -void sub_uninit(struct sh_sub *sh) +#define MAX_PACKETS 10 +#define MAX_BYTES 10000 + +struct sd_conv_buffer { + struct demux_packet pkt[MAX_PACKETS]; + int num_pkt; + int read_pkt; + char buffer[MAX_BYTES]; + int cur_buffer; +}; + +void sd_conv_add_packet(struct sd *sd, void *data, int data_len, double pts, + double duration) +{ + if (!sd->sd_conv_buffer) + sd->sd_conv_buffer = talloc_zero(sd, struct sd_conv_buffer); + struct sd_conv_buffer *buf = sd->sd_conv_buffer; + if (buf->num_pkt >= MAX_PACKETS || buf->cur_buffer + data_len + 1 > MAX_BYTES) + goto out_of_space; + if (buf->read_pkt == buf->num_pkt) + sd_conv_def_reset(sd); + assert(buf->read_pkt == 0); // no mixing of reading/adding allowed + struct demux_packet *pkt = &buf->pkt[buf->num_pkt++]; + *pkt = (struct demux_packet) { + .buffer = &buf->buffer[buf->cur_buffer], + .len = data_len, + .pts = pts, + .duration = duration, + }; + memcpy(pkt->buffer, data, data_len); + pkt->buffer[data_len] = 0; + buf->cur_buffer += data_len + 1; + return; + +out_of_space: + mp_msg(MSGT_OSD, MSGL_ERR, "Subtitle too big.\n"); +} + +struct demux_packet *sd_conv_def_get_converted(struct sd *sd) { - assert (!sh->active); - if (sh->initialized && sh->sd_driver->uninit) - sh->sd_driver->uninit(sh); - sh->initialized = false; + struct sd_conv_buffer *buf = sd->sd_conv_buffer; + if (buf && buf->read_pkt < buf->num_pkt) + return &buf->pkt[buf->read_pkt++]; + return NULL; +} + +void sd_conv_def_reset(struct sd *sd) +{ + struct sd_conv_buffer *buf = sd->sd_conv_buffer; + if (buf) { + buf->read_pkt = buf->num_pkt = 0; + buf->cur_buffer = 0; + } } |