Ticket #3118: 0001-avformat-basic-language-support-in-SAMI-subtitles.patch

File 0001-avformat-basic-language-support-in-SAMI-subtitles.patch, 12.2 KB (added by Jehan, 10 years ago)

Basic language support in SAMI files.

  • libavformat/realtextdec.c

    From 0cd4d92b98ecbf364891038b851740291cf75219 Mon Sep 17 00:00:00 2001
    From: Jehan <jehan@girinstud.io>
    Date: Wed, 28 Sep 2016 03:28:52 +0200
    Subject: [PATCH] avformat: basic language support in SAMI subtitles.
    
    Different languages are not yet extracted as separate subtitle tracks.
    This first basic version simply uses the default language and ignore any
    others. The spec says: "If the user (or author) has not explicitly
    selected a language, the first Class (language) definition will be used
    by default."
    See: https://msdn.microsoft.com/en-us/library/ms971327.aspx
    ---
     libavformat/realtextdec.c |  14 ++--
     libavformat/samidec.c     | 167 ++++++++++++++++++++++++++++++++++++++++++----
     libavformat/subtitles.c   |  22 +++++-
     libavformat/subtitles.h   |   5 +-
     4 files changed, 187 insertions(+), 21 deletions(-)
    
    diff --git a/libavformat/realtextdec.c b/libavformat/realtextdec.c
    index 618d4f7..c0b0e44 100644
    a b static int realtext_read_header(AVFormatContext *s)  
    8585
    8686        if (!av_strncasecmp(buf.str, "<window", 7)) {
    8787            /* save header to extradata */
    88             const char *p = ff_smil_get_attr_ptr(buf.str, "duration");
     88            char *p = ff_smil_get_attr_ptr(buf.str, "duration");
    8989
    90             if (p)
     90            if (p) {
    9191                duration = read_ts(p);
     92                av_free(p);
     93            }
    9294            st->codecpar->extradata = av_strdup(buf.str);
    9395            if (!st->codecpar->extradata) {
    9496                res = AVERROR(ENOMEM);
    static int realtext_read_header(AVFormatContext *s)  
    105107                goto end;
    106108            }
    107109            if (!merge) {
    108                 const char *begin = ff_smil_get_attr_ptr(buf.str, "begin");
    109                 const char *end   = ff_smil_get_attr_ptr(buf.str, "end");
     110                char *begin = ff_smil_get_attr_ptr(buf.str, "begin");
     111                char *end   = ff_smil_get_attr_ptr(buf.str, "end");
    110112
    111113                sub->pos      = pos;
    112114                sub->pts      = begin ? read_ts(begin) : 0;
    113115                sub->duration = end ? (read_ts(end) - sub->pts) : duration;
     116                if (begin)
     117                    av_free(begin);
     118                if (end)
     119                    av_free(end);
    114120            }
    115121        }
    116122        av_bprint_clear(&buf);
  • libavformat/samidec.c

    diff --git a/libavformat/samidec.c b/libavformat/samidec.c
    index 7ea1bdf..3bd16ea 100644
    a b  
    2626
    2727#include "avformat.h"
    2828#include "internal.h"
     29#include "regex.h"
    2930#include "subtitles.h"
    3031#include "libavcodec/internal.h"
    3132#include "libavutil/avstring.h"
     
    3435
    3536typedef struct {
    3637    FFDemuxSubtitlesQueue q;
     38    char **lang_names;
     39    char **lang_codes;
     40    char **lang_ids;
     41    int    n_langs;
    3742} SAMIContext;
    3843
    3944static int sami_probe(AVProbeData *p)
    static int sami_read_header(AVFormatContext *s)  
    5156    SAMIContext *sami = s->priv_data;
    5257    AVStream *st = avformat_new_stream(s, NULL);
    5358    AVBPrint buf, hdr_buf;
     59    int is_style = 0;
    5460    char c = 0;
    5561    int res = 0, got_first_sync_point = 0;
     62    int ignore_sub;
    5663    FFTextReader tr;
     64    int64_t last_pos;
     65    int64_t last_pts;
     66    int     new_sub = 0;
     67
    5768    ff_text_init_avio(s, &tr, s->pb);
    5869
     70    sami->n_langs = 0;
     71
    5972    if (!st)
    6073        return AVERROR(ENOMEM);
    6174    avpriv_set_pts_info(st, 64, 1, 1000);
    static int sami_read_header(AVFormatContext *s)  
    6578    av_bprint_init(&buf,     0, AV_BPRINT_SIZE_UNLIMITED);
    6679    av_bprint_init(&hdr_buf, 0, AV_BPRINT_SIZE_UNLIMITED);
    6780
     81    /* Read header */
     82    while (!ff_text_eof(&tr)) {
     83        int header_end;
     84        int n = ff_smil_extract_next_text_chunk(&tr, &buf, &c);
     85
     86        if (n == 0)
     87            break;
     88
     89        header_end = !av_strncasecmp(buf.str, "</Head", 6);
     90        if (header_end) {
     91             av_bprint_clear(&buf);
     92             break;
     93        }
     94
     95        if (!av_strncasecmp(buf.str, "<Style", 6)) {
     96            is_style = 1;
     97            av_bprint_clear(&buf);
     98            continue;
     99        }
     100
     101        if (is_style) {
     102            if (!av_strncasecmp(buf.str, "</Style", 7)) {
     103                is_style = 0;
     104                av_bprint_clear(&buf);
     105                continue;
     106            }
     107            else {
     108                regex_t    regex;
     109                regmatch_t matchptr[4];
     110
     111                if (!regcomp(&regex, "\\.\\([a-z]\\+\\)\\s*{\\s*Name:\\s*\\([^;}]\\+\\);\\s*Lang:\\s*\\([^;}]\\+\\)", REG_ICASE)) {
     112                    char *style_text = buf.str;
     113
     114                    while (!regexec(&regex, style_text, 4, matchptr, REG_ICASE)) {
     115                        char **lang_names;
     116                        char **lang_codes;
     117                        char **lang_ids;
     118
     119                        lang_names = av_realloc(sami->lang_names,
     120                                                (sami->n_langs + 1) * sizeof(char*));
     121                        lang_codes = av_realloc(sami->lang_codes,
     122                                                (sami->n_langs + 1) * sizeof(char*));
     123                        lang_ids   = av_realloc(sami->lang_ids,
     124                                                (sami->n_langs + 1) * sizeof(char*));
     125                        if (lang_names && lang_codes && lang_ids) {
     126                            char *lang_name;
     127                            char *lang_code;
     128                            char *lang_id;
     129
     130                            lang_name = av_strndup(style_text + matchptr[2].rm_so,
     131                                                   matchptr[2].rm_eo - matchptr[2].rm_so);
     132
     133                            lang_code = av_strndup(style_text + matchptr[3].rm_so,
     134                                                   matchptr[3].rm_eo - matchptr[3].rm_so);
     135
     136                            lang_id = av_strndup(style_text + matchptr[1].rm_so,
     137                                                 matchptr[1].rm_eo - matchptr[1].rm_so);
     138
     139                            lang_names[sami->n_langs] = lang_name;
     140                            lang_codes[sami->n_langs] = lang_code;
     141                            lang_ids[sami->n_langs++] = lang_id;
     142
     143                            sami->lang_names = lang_names;
     144                            sami->lang_codes = lang_codes;
     145                            sami->lang_ids = lang_ids;
     146                        }
     147                        style_text = style_text + matchptr[0].rm_eo;
     148                    }
     149                }
     150                regfree(&regex);
     151            }
     152        }
     153        av_bprint_clear(&buf);
     154    }
     155
     156    /* Read body. */
    68157    while (!ff_text_eof(&tr)) {
    69158        AVPacket *sub;
    70159        const int64_t pos = ff_text_pos(&tr) - (c != 0);
    71         int is_sync, is_body, n = ff_smil_extract_next_text_chunk(&tr, &buf, &c);
     160        int is_sync, is_body, is_p;
     161        int n = ff_smil_extract_next_text_chunk(&tr, &buf, &c);
    72162
    73163        if (n == 0)
    74164            break;
    static int sami_read_header(AVFormatContext *s)  
    80170        }
    81171
    82172        is_sync = !av_strncasecmp(buf.str, "<SYNC", 5);
    83         if (is_sync)
     173        if (is_sync) {
     174            char *p = ff_smil_get_attr_ptr(buf.str, "Start");
     175
    84176            got_first_sync_point = 1;
     177            ignore_sub = 0;
     178            new_sub = 1;
     179
     180            last_pos = pos;
     181            last_pts = p ? strtol(p, NULL, 10) : 0;
     182            if (p)
     183                av_free(p);
     184        }
     185
     186        is_p = !av_strncasecmp(buf.str, "<P", 2);
     187        if (is_p) {
     188            char *class = ff_smil_get_attr_ptr(buf.str, "Class");
     189            if (class && !sami->n_langs) {
     190                /* If no languages were set in <STYLE>, let's just
     191                 * assume that the first encountered Class is the
     192                 * default language. */
     193                sami->lang_names = av_malloc(sizeof(char*));
     194                sami->lang_codes = av_malloc(sizeof(char*));
     195                sami->lang_ids   = av_malloc(sizeof(char*));
     196                sami->lang_names[0] = av_strdup(class);
     197                sami->lang_codes[0] = av_strdup(class);
     198                sami->lang_ids[0] = av_strdup(class);
     199                sami->n_langs = 1;
     200            }
     201            if (class && av_strcasecmp(sami->lang_ids[0], class)) {
     202                /* Ignore subtitle for a different language. */
     203                ignore_sub = 1;
     204            }
     205            else {
     206                ignore_sub = 0;
     207            }
     208            if (class)
     209                av_free(class);
     210        }
    85211
    86212        if (!got_first_sync_point) {
    87213            av_bprintf(&hdr_buf, "%s", buf.str);
    88214        } else {
    89             sub = ff_subtitles_queue_insert(&sami->q, buf.str, buf.len, !is_sync);
    90             if (!sub) {
    91                 res = AVERROR(ENOMEM);
    92                 goto end;
    93             }
    94             if (is_sync) {
    95                 const char *p = ff_smil_get_attr_ptr(buf.str, "Start");
    96                 sub->pos      = pos;
    97                 sub->pts      = p ? strtol(p, NULL, 10) : 0;
    98                 sub->duration = -1;
     215            if (!ignore_sub && !is_sync) {
     216                sub = ff_subtitles_queue_insert(&sami->q, buf.str, buf.len, !new_sub);
     217                if (!sub) {
     218                    res = AVERROR(ENOMEM);
     219                    goto end;
     220                }
     221                if (new_sub) {
     222                    new_sub = 0;
     223                    sub->duration = -1;
     224                    sub->pos = last_pos;
     225                    sub->pts = last_pts;
     226                }
    99227            }
    100228        }
    101229        av_bprint_clear(&buf);
    static int sami_read_close(AVFormatContext *s)  
    130258{
    131259    SAMIContext *sami = s->priv_data;
    132260    ff_subtitles_queue_clean(&sami->q);
     261    if (sami->lang_names) {
     262        for (int i = 0; i < sami->n_langs; i++)
     263            av_free(sami->lang_names[i]);
     264        av_free(sami->lang_names);
     265    }
     266    if (sami->lang_codes) {
     267        for (int i = 0; i < sami->n_langs; i++)
     268            av_free(sami->lang_codes[i]);
     269        av_free(sami->lang_codes);
     270    }
     271    if (sami->lang_ids) {
     272        for (int i = 0; i < sami->n_langs; i++)
     273            av_free(sami->lang_ids[i]);
     274        av_free(sami->lang_ids);
     275    }
    133276    return 0;
    134277}
    135278
  • libavformat/subtitles.c

    diff --git a/libavformat/subtitles.c b/libavformat/subtitles.c
    index 108f909..4d22daf 100644
    a b int ff_smil_extract_next_text_chunk(FFTextReader *tr, AVBPrint *buf, char *c)  
    327327    return i;
    328328}
    329329
    330 const char *ff_smil_get_attr_ptr(const char *s, const char *attr)
     330char *ff_smil_get_attr_ptr(const char *s, const char *attr)
    331331{
    332332    int in_quotes = 0;
    333333    const size_t len = strlen(attr);
    const char *ff_smil_get_attr_ptr(const char *s, const char *attr)  
    341341        }
    342342        while (av_isspace(*s))
    343343            s++;
    344         if (!av_strncasecmp(s, attr, len) && s[len] == '=')
    345             return s + len + 1 + (s[len + 1] == '"');
     344        if (!av_strncasecmp(s, attr, len) && s[len] == '=') {
     345            const char *value = NULL;
     346            int quoted_value = 0;
     347
     348            quoted_value = (s[len + 1] == '"');
     349            value = s + len + 1 + quoted_value;
     350            s = value;
     351
     352            while (*s) {
     353                if ((quoted_value && *s == '"') ||
     354                    (!quoted_value && (av_isspace(*s) || *s == '>')) ||
     355                    *s == '\0')
     356                {
     357                    return av_strndup(value, s - value);
     358                }
     359                s++;
     360            }
     361        }
    346362    }
    347363    return NULL;
    348364}
  • libavformat/subtitles.h

    diff --git a/libavformat/subtitles.h b/libavformat/subtitles.h
    index ca78db2..0e1a2ef 100644
    a b void ff_subtitles_queue_clean(FFDemuxSubtitlesQueue *q);  
    151151int ff_smil_extract_next_text_chunk(FFTextReader *tr, AVBPrint *buf, char *c);
    152152
    153153/**
    154  * SMIL helper to point on the value of an attribute in the given tag.
     154 * SMIL helper returning a newly allocated copy of the value of an
     155 * attribute in the given tag.
    155156 *
    156157 * @param s    SMIL tag ("<...>")
    157158 * @param attr the attribute to look for
    158159 */
    159 const char *ff_smil_get_attr_ptr(const char *s, const char *attr);
     160char *ff_smil_get_attr_ptr(const char *s, const char *attr);
    160161
    161162/**
    162163 * @brief Same as ff_subtitles_read_text_chunk(), but read from an AVIOContext.