From 3000df35d31f09f13a7c662e2f96bcd7d0f6ac13 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 1 Jun 2013 19:44:55 +0200 Subject: sub: basic subtitle converters Add a basic infrastructure for subtitle converters. These converters work sort-of like decoders, except that they produce packets instead of subtitle bitmaps. They are put in front of actual decoders. Start with sd_movtext. 4 lines of code are blown up to a 55 lines file, but fortunately this is not going to be that bad for the following converters. --- sub/dec_sub.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 141 insertions(+), 29 deletions(-) (limited to 'sub/dec_sub.c') diff --git a/sub/dec_sub.c b/sub/dec_sub.c index 67828921b4..6ef1e4a8cf 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -26,10 +26,12 @@ #include "sub/sub.h" #include "sub/dec_sub.h" #include "core/options.h" +#include "core/mp_msg.h" extern const struct sd_functions sd_ass; extern const struct sd_functions sd_lavc; extern const struct sd_functions sd_spu; +extern const struct sd_functions sd_movtext; static const struct sd_functions *sd_list[] = { #ifdef CONFIG_ASS @@ -37,14 +39,18 @@ static const struct sd_functions *sd_list[] = { #endif &sd_lavc, &sd_spu, + &sd_movtext, NULL }; +#define MAX_NUM_SD 2 + struct dec_sub { struct MPOpts *opts; struct sd init_sd; - struct sd *sd; + struct sd *sd[MAX_NUM_SD]; + int num_sd; }; struct dec_sub *sub_create(struct MPOpts *opts) @@ -54,24 +60,33 @@ struct dec_sub *sub_create(struct MPOpts *opts) return sub; } +static void sub_uninit(struct dec_sub *sub) +{ + sub_reset(sub); + for (int n = 0; n < sub->num_sd; n++) { + if (sub->sd[n]->driver->uninit) + sub->sd[n]->driver->uninit(sub->sd[n]); + talloc_free(sub->sd[n]); + } + sub->num_sd = 0; +} + void sub_destroy(struct dec_sub *sub) { if (!sub) return; - if (sub->sd && sub->sd->driver->uninit) - sub->sd->driver->uninit(sub->sd); - talloc_free(sub->sd); + sub_uninit(sub); talloc_free(sub); } bool sub_is_initialized(struct dec_sub *sub) { - return !!sub->sd; + return !!sub->num_sd; } -struct sd *sub_get_sd(struct dec_sub *sub) +struct sd *sub_get_last_sd(struct dec_sub *sub) { - return sub->sd; + return sub->num_sd ? sub->sd[sub->num_sd - 1] : NULL; } void sub_set_video_res(struct dec_sub *sub, int w, int h) @@ -114,62 +129,159 @@ static int sub_init_decoder(struct dec_sub *sub, struct sd *sd) void sub_init_from_sh(struct dec_sub *sub, struct sh_sub *sh) { - assert(!sub->sd); + assert(!sub->num_sd); + if (sh->extradata && !sub->init_sd.extradata) sub_set_extradata(sub, sh->extradata, sh->extradata_len); - struct sd *sd = talloc(NULL, struct sd); - *sd = sub->init_sd; - sd->opts = sub->opts; - sd->codec = sh->gsh->codec; - sd->ass_track = sh->track; - if (sub_init_decoder(sub, sd) < 0) { - talloc_free(sd); - sd = NULL; + struct sd init_sd = sub->init_sd; + init_sd.codec = sh->gsh->codec; + init_sd.ass_track = sh->track; + + while (sub->num_sd < MAX_NUM_SD) { + struct sd *sd = talloc(NULL, struct sd); + *sd = init_sd; + sd->opts = sub->opts; + if (sub_init_decoder(sub, sd) < 0) { + talloc_free(sd); + break; + } + sub->sd[sub->num_sd] = sd; + sub->num_sd++; + // Try adding new converters until a decoder is reached + if (sd->driver->get_bitmaps || sd->driver->get_text) + return; + init_sd = (struct sd) { + .codec = sd->output_codec, + .extradata = sd->output_extradata, + .extradata_len = sd->output_extradata_len, + .ass_library = sub->init_sd.ass_library, + .ass_renderer = sub->init_sd.ass_renderer, + }; } - sub->sd = sd; + + sub_uninit(sub); + mp_msg(MSGT_OSD, MSGL_ERR, "Could not find subtitle decoder for format '%s'.\n", + sh->gsh->codec ? sh->gsh->codec : ""); } bool sub_accept_packets_in_advance(struct dec_sub *sub) { - return sub->sd && sub->sd->driver->accept_packets_in_advance; + // Converters are assumed to always accept packets in advance + struct sd *sd = sub_get_last_sd(sub); + return sd && sd->driver->accept_packets_in_advance; +} + +static void decode_next(struct dec_sub *sub, int n, struct demux_packet *packet) +{ + struct sd *sd = sub->sd[n]; + sd->driver->decode(sd, packet); + if (n + 1 >= sub->num_sd || !sd->driver->get_converted) + return; + while (1) { + struct demux_packet *next = + sd->driver->get_converted ? sd->driver->get_converted(sd) : NULL; + if (!next) + break; + decode_next(sub, n + 1, next); + } } void sub_decode(struct dec_sub *sub, struct demux_packet *packet) { - if (sub->sd) - sub->sd->driver->decode(sub->sd, packet); + if (sub->num_sd > 0) + decode_next(sub, 0, packet); } void sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, double pts, struct sub_bitmaps *res) { struct MPOpts *opts = sub->opts; + struct sd *sd = sub_get_last_sd(sub); *res = (struct sub_bitmaps) {0}; - if (sub->sd && opts->sub_visibility) { - if (sub->sd->driver->get_bitmaps) - sub->sd->driver->get_bitmaps(sub->sd, dim, pts, res); + if (sd && opts->sub_visibility) { + if (sd->driver->get_bitmaps) + sd->driver->get_bitmaps(sd, dim, pts, res); } } bool sub_has_get_text(struct dec_sub *sub) { - return sub->sd && sub->sd->driver->get_text; + struct sd *sd = sub_get_last_sd(sub); + return sd && sd->driver->get_text; } char *sub_get_text(struct dec_sub *sub, double pts) { struct MPOpts *opts = sub->opts; + struct sd *sd = sub_get_last_sd(sub); char *text = NULL; - if (sub->sd && opts->sub_visibility) { - if (sub->sd->driver->get_text) - text = sub->sd->driver->get_text(sub->sd, pts); + if (sd && opts->sub_visibility) { + if (sd->driver->get_text) + text = sd->driver->get_text(sd, pts); } return text; } void sub_reset(struct dec_sub *sub) { - if (sub->sd && sub->sd->driver->reset) - sub->sd->driver->reset(sub->sd); + for (int n = 0; n < sub->num_sd; n++) { + if (sub->sd[n]->driver->reset) + sub->sd[n]->driver->reset(sub->sd[n]); + } +} + +#define MAX_PACKETS 10 +#define MAX_BYTES 10000 + +struct sd_conv_buffer { + struct demux_packet pkt[MAX_PACKETS]; + int num_pkt; + int read_pkt; + char buffer[MAX_BYTES]; + int cur_buffer; +}; + +void sd_conv_add_packet(struct sd *sd, void *data, int data_len, double pts, + double duration) +{ + if (!sd->sd_conv_buffer) + sd->sd_conv_buffer = talloc_zero(sd, struct sd_conv_buffer); + struct sd_conv_buffer *buf = sd->sd_conv_buffer; + if (buf->num_pkt >= MAX_PACKETS || buf->cur_buffer + data_len + 1 > MAX_BYTES) + goto out_of_space; + if (buf->read_pkt == buf->num_pkt) + sd_conv_def_reset(sd); + assert(buf->read_pkt == 0); // no mixing of reading/adding allowed + struct demux_packet *pkt = &buf->pkt[buf->num_pkt++]; + *pkt = (struct demux_packet) { + .buffer = &buf->buffer[buf->cur_buffer], + .len = data_len, + .pts = pts, + .duration = duration, + }; + memcpy(pkt->buffer, data, data_len); + pkt->buffer[data_len] = 0; + buf->cur_buffer += data_len + 1; + return; + +out_of_space: + mp_msg(MSGT_OSD, MSGL_ERR, "Subtitle too big.\n"); +} + +struct demux_packet *sd_conv_def_get_converted(struct sd *sd) +{ + struct sd_conv_buffer *buf = sd->sd_conv_buffer; + if (buf && buf->read_pkt < buf->num_pkt) + return &buf->pkt[buf->read_pkt++]; + return NULL; +} + +void sd_conv_def_reset(struct sd *sd) +{ + struct sd_conv_buffer *buf = sd->sd_conv_buffer; + if (buf) { + buf->read_pkt = buf->num_pkt = 0; + buf->cur_buffer = 0; + } } -- cgit v1.2.3