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 , 10 years ago) |
|---|
-
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) 85 85 86 86 if (!av_strncasecmp(buf.str, "<window", 7)) { 87 87 /* save header to extradata */ 88 c onst char *p = ff_smil_get_attr_ptr(buf.str, "duration");88 char *p = ff_smil_get_attr_ptr(buf.str, "duration"); 89 89 90 if (p) 90 if (p) { 91 91 duration = read_ts(p); 92 av_free(p); 93 } 92 94 st->codecpar->extradata = av_strdup(buf.str); 93 95 if (!st->codecpar->extradata) { 94 96 res = AVERROR(ENOMEM); … … static int realtext_read_header(AVFormatContext *s) 105 107 goto end; 106 108 } 107 109 if (!merge) { 108 c onst char *begin = ff_smil_get_attr_ptr(buf.str, "begin");109 c onst 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"); 110 112 111 113 sub->pos = pos; 112 114 sub->pts = begin ? read_ts(begin) : 0; 113 115 sub->duration = end ? (read_ts(end) - sub->pts) : duration; 116 if (begin) 117 av_free(begin); 118 if (end) 119 av_free(end); 114 120 } 115 121 } 116 122 av_bprint_clear(&buf); -
libavformat/samidec.c
diff --git a/libavformat/samidec.c b/libavformat/samidec.c index 7ea1bdf..3bd16ea 100644
a b 26 26 27 27 #include "avformat.h" 28 28 #include "internal.h" 29 #include "regex.h" 29 30 #include "subtitles.h" 30 31 #include "libavcodec/internal.h" 31 32 #include "libavutil/avstring.h" … … 34 35 35 36 typedef struct { 36 37 FFDemuxSubtitlesQueue q; 38 char **lang_names; 39 char **lang_codes; 40 char **lang_ids; 41 int n_langs; 37 42 } SAMIContext; 38 43 39 44 static int sami_probe(AVProbeData *p) … … static int sami_read_header(AVFormatContext *s) 51 56 SAMIContext *sami = s->priv_data; 52 57 AVStream *st = avformat_new_stream(s, NULL); 53 58 AVBPrint buf, hdr_buf; 59 int is_style = 0; 54 60 char c = 0; 55 61 int res = 0, got_first_sync_point = 0; 62 int ignore_sub; 56 63 FFTextReader tr; 64 int64_t last_pos; 65 int64_t last_pts; 66 int new_sub = 0; 67 57 68 ff_text_init_avio(s, &tr, s->pb); 58 69 70 sami->n_langs = 0; 71 59 72 if (!st) 60 73 return AVERROR(ENOMEM); 61 74 avpriv_set_pts_info(st, 64, 1, 1000); … … static int sami_read_header(AVFormatContext *s) 65 78 av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); 66 79 av_bprint_init(&hdr_buf, 0, AV_BPRINT_SIZE_UNLIMITED); 67 80 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(®ex, "\\.\\([a-z]\\+\\)\\s*{\\s*Name:\\s*\\([^;}]\\+\\);\\s*Lang:\\s*\\([^;}]\\+\\)", REG_ICASE)) { 112 char *style_text = buf.str; 113 114 while (!regexec(®ex, 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(®ex); 151 } 152 } 153 av_bprint_clear(&buf); 154 } 155 156 /* Read body. */ 68 157 while (!ff_text_eof(&tr)) { 69 158 AVPacket *sub; 70 159 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); 72 162 73 163 if (n == 0) 74 164 break; … … static int sami_read_header(AVFormatContext *s) 80 170 } 81 171 82 172 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 84 176 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 } 85 211 86 212 if (!got_first_sync_point) { 87 213 av_bprintf(&hdr_buf, "%s", buf.str); 88 214 } 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 } 99 227 } 100 228 } 101 229 av_bprint_clear(&buf); … … static int sami_read_close(AVFormatContext *s) 130 258 { 131 259 SAMIContext *sami = s->priv_data; 132 260 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 } 133 276 return 0; 134 277 } 135 278 -
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) 327 327 return i; 328 328 } 329 329 330 c onst char *ff_smil_get_attr_ptr(const char *s, const char *attr)330 char *ff_smil_get_attr_ptr(const char *s, const char *attr) 331 331 { 332 332 int in_quotes = 0; 333 333 const size_t len = strlen(attr); … … const char *ff_smil_get_attr_ptr(const char *s, const char *attr) 341 341 } 342 342 while (av_isspace(*s)) 343 343 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 } 346 362 } 347 363 return NULL; 348 364 } -
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); 151 151 int ff_smil_extract_next_text_chunk(FFTextReader *tr, AVBPrint *buf, char *c); 152 152 153 153 /** 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. 155 156 * 156 157 * @param s SMIL tag ("<...>") 157 158 * @param attr the attribute to look for 158 159 */ 159 c onst char *ff_smil_get_attr_ptr(const char *s, const char *attr);160 char *ff_smil_get_attr_ptr(const char *s, const char *attr); 160 161 161 162 /** 162 163 * @brief Same as ff_subtitles_read_text_chunk(), but read from an AVIOContext.
