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 +++++++++++++++++++++++++++++++++++++++++++++---------- sub/dec_sub.h | 2 +- sub/sd.h | 23 +++++++- sub/sd_ass.c | 5 +- sub/sd_movtext.c | 54 ++++++++++++++++++ 5 files changed, 219 insertions(+), 35 deletions(-) create mode 100644 sub/sd_movtext.c (limited to 'sub') diff --git a/sub/dec_sub.c b/sub/dec_sub.c index 67828921b4..6ef1e4a8cf 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -26,10 +26,12 @@ #include "sub/sub.h" #include "sub/dec_sub.h" #include "core/options.h" +#include "core/mp_msg.h" extern const struct sd_functions sd_ass; extern const struct sd_functions sd_lavc; extern const struct sd_functions sd_spu; +extern const struct sd_functions sd_movtext; static const struct sd_functions *sd_list[] = { #ifdef CONFIG_ASS @@ -37,14 +39,18 @@ static const struct sd_functions *sd_list[] = { #endif &sd_lavc, &sd_spu, + &sd_movtext, NULL }; +#define MAX_NUM_SD 2 + struct dec_sub { struct MPOpts *opts; struct sd init_sd; - struct sd *sd; + struct sd *sd[MAX_NUM_SD]; + int num_sd; }; struct dec_sub *sub_create(struct MPOpts *opts) @@ -54,24 +60,33 @@ struct dec_sub *sub_create(struct MPOpts *opts) return sub; } +static void sub_uninit(struct dec_sub *sub) +{ + sub_reset(sub); + for (int n = 0; n < sub->num_sd; n++) { + if (sub->sd[n]->driver->uninit) + sub->sd[n]->driver->uninit(sub->sd[n]); + talloc_free(sub->sd[n]); + } + sub->num_sd = 0; +} + void sub_destroy(struct dec_sub *sub) { if (!sub) return; - if (sub->sd && sub->sd->driver->uninit) - sub->sd->driver->uninit(sub->sd); - talloc_free(sub->sd); + sub_uninit(sub); talloc_free(sub); } bool sub_is_initialized(struct dec_sub *sub) { - return !!sub->sd; + return !!sub->num_sd; } -struct sd *sub_get_sd(struct dec_sub *sub) +struct sd *sub_get_last_sd(struct dec_sub *sub) { - return sub->sd; + return sub->num_sd ? sub->sd[sub->num_sd - 1] : NULL; } void sub_set_video_res(struct dec_sub *sub, int w, int h) @@ -114,62 +129,159 @@ static int sub_init_decoder(struct dec_sub *sub, struct sd *sd) void sub_init_from_sh(struct dec_sub *sub, struct sh_sub *sh) { - assert(!sub->sd); + assert(!sub->num_sd); + if (sh->extradata && !sub->init_sd.extradata) sub_set_extradata(sub, sh->extradata, sh->extradata_len); - struct sd *sd = talloc(NULL, struct sd); - *sd = sub->init_sd; - sd->opts = sub->opts; - sd->codec = sh->gsh->codec; - sd->ass_track = sh->track; - if (sub_init_decoder(sub, sd) < 0) { - talloc_free(sd); - sd = NULL; + struct sd init_sd = sub->init_sd; + init_sd.codec = sh->gsh->codec; + init_sd.ass_track = sh->track; + + while (sub->num_sd < MAX_NUM_SD) { + struct sd *sd = talloc(NULL, struct sd); + *sd = init_sd; + sd->opts = sub->opts; + if (sub_init_decoder(sub, sd) < 0) { + talloc_free(sd); + break; + } + sub->sd[sub->num_sd] = sd; + sub->num_sd++; + // Try adding new converters until a decoder is reached + if (sd->driver->get_bitmaps || sd->driver->get_text) + return; + init_sd = (struct sd) { + .codec = sd->output_codec, + .extradata = sd->output_extradata, + .extradata_len = sd->output_extradata_len, + .ass_library = sub->init_sd.ass_library, + .ass_renderer = sub->init_sd.ass_renderer, + }; } - sub->sd = sd; + + sub_uninit(sub); + mp_msg(MSGT_OSD, MSGL_ERR, "Could not find subtitle decoder for format '%s'.\n", + sh->gsh->codec ? sh->gsh->codec : ""); } bool sub_accept_packets_in_advance(struct dec_sub *sub) { - return sub->sd && sub->sd->driver->accept_packets_in_advance; + // Converters are assumed to always accept packets in advance + struct sd *sd = sub_get_last_sd(sub); + return sd && sd->driver->accept_packets_in_advance; +} + +static void decode_next(struct dec_sub *sub, int n, struct demux_packet *packet) +{ + struct sd *sd = sub->sd[n]; + sd->driver->decode(sd, packet); + if (n + 1 >= sub->num_sd || !sd->driver->get_converted) + return; + while (1) { + struct demux_packet *next = + sd->driver->get_converted ? sd->driver->get_converted(sd) : NULL; + if (!next) + break; + decode_next(sub, n + 1, next); + } } void sub_decode(struct dec_sub *sub, struct demux_packet *packet) { - if (sub->sd) - sub->sd->driver->decode(sub->sd, packet); + if (sub->num_sd > 0) + decode_next(sub, 0, packet); } void sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, double pts, struct sub_bitmaps *res) { struct MPOpts *opts = sub->opts; + struct sd *sd = sub_get_last_sd(sub); *res = (struct sub_bitmaps) {0}; - if (sub->sd && opts->sub_visibility) { - if (sub->sd->driver->get_bitmaps) - sub->sd->driver->get_bitmaps(sub->sd, dim, pts, res); + if (sd && opts->sub_visibility) { + if (sd->driver->get_bitmaps) + sd->driver->get_bitmaps(sd, dim, pts, res); } } bool sub_has_get_text(struct dec_sub *sub) { - return sub->sd && sub->sd->driver->get_text; + struct sd *sd = sub_get_last_sd(sub); + return sd && sd->driver->get_text; } char *sub_get_text(struct dec_sub *sub, double pts) { struct MPOpts *opts = sub->opts; + struct sd *sd = sub_get_last_sd(sub); char *text = NULL; - if (sub->sd && opts->sub_visibility) { - if (sub->sd->driver->get_text) - text = sub->sd->driver->get_text(sub->sd, pts); + if (sd && opts->sub_visibility) { + if (sd->driver->get_text) + text = sd->driver->get_text(sd, pts); } return text; } void sub_reset(struct dec_sub *sub) { - if (sub->sd && sub->sd->driver->reset) - sub->sd->driver->reset(sub->sd); + for (int n = 0; n < sub->num_sd; n++) { + if (sub->sd[n]->driver->reset) + sub->sd[n]->driver->reset(sub->sd[n]); + } +} + +#define MAX_PACKETS 10 +#define MAX_BYTES 10000 + +struct sd_conv_buffer { + struct demux_packet pkt[MAX_PACKETS]; + int num_pkt; + int read_pkt; + char buffer[MAX_BYTES]; + int cur_buffer; +}; + +void sd_conv_add_packet(struct sd *sd, void *data, int data_len, double pts, + double duration) +{ + if (!sd->sd_conv_buffer) + sd->sd_conv_buffer = talloc_zero(sd, struct sd_conv_buffer); + struct sd_conv_buffer *buf = sd->sd_conv_buffer; + if (buf->num_pkt >= MAX_PACKETS || buf->cur_buffer + data_len + 1 > MAX_BYTES) + goto out_of_space; + if (buf->read_pkt == buf->num_pkt) + sd_conv_def_reset(sd); + assert(buf->read_pkt == 0); // no mixing of reading/adding allowed + struct demux_packet *pkt = &buf->pkt[buf->num_pkt++]; + *pkt = (struct demux_packet) { + .buffer = &buf->buffer[buf->cur_buffer], + .len = data_len, + .pts = pts, + .duration = duration, + }; + memcpy(pkt->buffer, data, data_len); + pkt->buffer[data_len] = 0; + buf->cur_buffer += data_len + 1; + return; + +out_of_space: + mp_msg(MSGT_OSD, MSGL_ERR, "Subtitle too big.\n"); +} + +struct demux_packet *sd_conv_def_get_converted(struct sd *sd) +{ + struct sd_conv_buffer *buf = sd->sd_conv_buffer; + if (buf && buf->read_pkt < buf->num_pkt) + return &buf->pkt[buf->read_pkt++]; + return NULL; +} + +void sd_conv_def_reset(struct sd *sd) +{ + struct sd_conv_buffer *buf = sd->sd_conv_buffer; + if (buf) { + buf->read_pkt = buf->num_pkt = 0; + buf->cur_buffer = 0; + } } diff --git a/sub/dec_sub.h b/sub/dec_sub.h index 39632d21a9..805a87ef5c 100644 --- a/sub/dec_sub.h +++ b/sub/dec_sub.h @@ -35,7 +35,7 @@ bool sub_has_get_text(struct dec_sub *sub); char *sub_get_text(struct dec_sub *sub, double pts); void sub_reset(struct dec_sub *sub); -struct sd *sub_get_sd(struct dec_sub *sub); +struct sd *sub_get_last_sd(struct dec_sub *sub); #ifdef CONFIG_ASS struct ass_track *sub_get_ass_track(struct dec_sub *sub); diff --git a/sub/sd.h b/sub/sd.h index 42f7b8a445..fadfe55edc 100644 --- a/sub/sd.h +++ b/sub/sd.h @@ -26,6 +26,14 @@ struct sd { // Shared renderer for ASS - done to avoid reloading embedded fonts. struct ass_library *ass_library; struct ass_renderer *ass_renderer; + + // Set by sub converter + const char *output_codec; + char *output_extradata; + int output_extradata_len; + + // Internal buffer for sd_conv_* functions + struct sd_conv_buffer *sd_conv_buffer; }; struct sd_functions { @@ -33,11 +41,22 @@ struct sd_functions { bool (*supports_format)(const char *format); int (*init)(struct sd *sd); void (*decode)(struct sd *sd, struct demux_packet *packet); + void (*reset)(struct sd *sd); + void (*uninit)(struct sd *sd); + + // decoder void (*get_bitmaps)(struct sd *sd, struct mp_osd_res dim, double pts, struct sub_bitmaps *res); char *(*get_text)(struct sd *sd, double pts); - void (*reset)(struct sd *sd); - void (*uninit)(struct sd *sd); + + // converter + struct demux_packet *(*get_converted)(struct sd *sd); }; +void sd_conv_add_packet(struct sd *sd, void *data, int data_len, double pts, + double duration); +struct demux_packet *sd_conv_def_get_converted(struct sd *sd); +void sd_conv_def_reset(struct sd *sd); +void sd_conv_def_uninit(struct sd *sd); + #endif diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 2ebd2164be..78adbf4863 100644 --- a/sub/sd_ass.c +++ b/sub/sd_ass.c @@ -53,8 +53,7 @@ static bool is_text_sub(const char *t) { return t && (is_ass_sub(t) || strcmp(t, "text") == 0 || - strcmp(t, "subrip") == 0 || - strcmp(t, "mov_text") == 0); + strcmp(t, "subrip") == 0); } static bool supports_format(const char *format) @@ -296,7 +295,7 @@ const struct sd_functions sd_ass = { struct ass_track *sub_get_ass_track(struct dec_sub *sub) { - struct sd *sd = sub_get_sd(sub); + struct sd *sd = sub_get_last_sd(sub); if (sd && sd->driver == &sd_ass && sd->priv) { struct sd_ass_priv *ctx = sd->priv; return ctx->ass_track; diff --git a/sub/sd_movtext.c b/sub/sd_movtext.c new file mode 100644 index 0000000000..30d2da8254 --- /dev/null +++ b/sub/sd_movtext.c @@ -0,0 +1,54 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mpv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mpv. If not, see . + */ + +#include +#include + +#include +#include + +#include "sd.h" + +static bool supports_format(const char *format) +{ + return format && strcmp(format, "mov_text") == 0; +} + +static int init(struct sd *sd) +{ + sd->output_codec = "text"; + return 0; +} + +static void decode(struct sd *sd, struct demux_packet *packet) +{ + unsigned char *data = packet->buffer; + int len = packet->len; + if (len < 2) + return; + len = FFMIN(len - 2, AV_RB16(data)); + data += 2; + sd_conv_add_packet(sd, data, len, packet->pts, packet->duration); +} + +const struct sd_functions sd_movtext = { + .supports_format = supports_format, + .init = init, + .decode = decode, + .get_converted = sd_conv_def_get_converted, + .reset = sd_conv_def_reset, +}; -- cgit v1.2.3