1 /*
2 * Copyright (C) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "gst_subtitle_srt_parse.h"
17 #include <cstring>
18 #include "securec.h"
19 #include "gst/gst.h"
20 #include "gst_subtitle_common.h"
21
22 namespace {
23 constexpr int32_t COLOR_VALUE_LEN = 7;
24 constexpr gsize MAX_BUFFER_SIZE = 100000000;
25 constexpr gsize MSEC_CONTAIN_BITS = 3;
26 constexpr guint SRT_MAX_OPEN_TAGS_NUM = 32;
27 }
28
29 #define PARENT_CLASS gst_subtitle_srt_parse_parent_class
30 G_DEFINE_TYPE(GstSubtitleSrtParse, gst_subtitle_srt_parse, GST_TYPE_SUBTITLE_BASE_PARSE);
31
32 static GstStaticCaps g_srtCaps = GST_STATIC_CAPS("application/x-subtitle-srt");
33 #define SRT_CAPS (gst_static_caps_get(&g_srtCaps))
34
35 static GstStaticPadTemplate g_sinkTempl = GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
36 GST_STATIC_CAPS("application/x-subtitle-srt"));
37
38 static GstStaticPadTemplate g_srcTempl = GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS,
39 GST_STATIC_CAPS("text/x-raw, format={pango-markup,utf8}"));
40
41 static gboolean gst_subtitle_srt_parse_decode_frame(GstSubtitleBaseParse *base, const GstSubtitleFrame *frame,
42 GstSubtitleDecodedFrame *decoded_frame);
43 static gsize gst_subtitle_srt_parse_read_frame(GstSubtitleBaseParse *base, GstSubtitleFrame *frame);
44 static GstCaps *gst_subtitle_srt_parse_get_src_caps(const GstSubtitleBaseParse *base, gint stream_id);
45 static void gst_subtitle_srt_parse_seek(GstSubtitleBaseParse *base, const GstEvent *event);
46 static void gst_subtitle_srt_parse_type_find(GstTypeFind *tf, gpointer priv);
47
gst_subtitle_srt_parse_dispose(GObject * object)48 static void gst_subtitle_srt_parse_dispose(GObject *object)
49 {
50 GstSubtitleSrtParse *parse = GST_SUBTITLE_SRT_PARSE_CAST(object);
51 g_return_if_fail(parse != nullptr);
52
53 GST_INFO_OBJECT(parse, "srt parse dispose in");
54 if (parse->buf != nullptr) {
55 g_warn_if_fail(g_string_free(parse->buf, (gboolean)TRUE) == nullptr);
56 parse->buf = nullptr;
57 }
58
59 G_OBJECT_CLASS(PARENT_CLASS)->dispose(object);
60 GST_INFO_OBJECT(parse, "srt parse dispose out");
61 }
62
gst_subtitle_srt_parse_class_init(GstSubtitleSrtParseClass * kclass)63 static void gst_subtitle_srt_parse_class_init(GstSubtitleSrtParseClass *kclass)
64 {
65 g_return_if_fail(kclass != nullptr);
66
67 GstElementClass *element_class = GST_ELEMENT_CLASS(kclass);
68 GObjectClass *object_class = G_OBJECT_CLASS(kclass);
69 GstSubtitleBaseParseClass *baseclass = GST_SUBTITLE_BASE_PARSE_CLASS(kclass);
70
71 gst_element_class_set_static_metadata(element_class, "srt test based on baseclass", "Codec/Parser/Subtitle",
72 "Parses srt subtitle", "OpenHarmony");
73 gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&g_sinkTempl));
74 gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&g_srcTempl));
75
76 baseclass->decode_frame_pfn = gst_subtitle_srt_parse_decode_frame;
77 baseclass->read_frame_pfn = gst_subtitle_srt_parse_read_frame;
78 baseclass->get_srcpad_caps_pfn = gst_subtitle_srt_parse_get_src_caps;
79 baseclass->on_seek_pfn = gst_subtitle_srt_parse_seek;
80
81 object_class->dispose = gst_subtitle_srt_parse_dispose;
82 }
83
gst_subtitle_srt_parse_init(GstSubtitleSrtParse * parse)84 static void gst_subtitle_srt_parse_init(GstSubtitleSrtParse *parse)
85 {
86 g_return_if_fail(parse != nullptr);
87
88 GST_INFO_OBJECT(parse, "gst_subtitle_srt_parse_init in");
89
90 parse->state = SUBNUM_STATE;
91 if (parse->buf != nullptr) {
92 g_warn_if_fail(g_string_truncate(parse->buf, 0) != nullptr);
93 } else {
94 parse->buf = g_string_new(nullptr);
95 if (parse->buf == nullptr) {
96 GST_WARNING_OBJECT(parse, "g_string_new failed");
97 } else {
98 if (parse->buf->str == nullptr) {
99 (void)g_string_free(parse->buf, (gboolean)TRUE);
100 parse->buf = nullptr;
101 }
102 }
103 }
104 parse->last_end = 0;
105
106 GST_INFO_OBJECT(parse, "gst_subtitle_srt_parse_init out");
107 }
108
gst_subtitle_srt_parse_decode_frame(GstSubtitleBaseParse * base,const GstSubtitleFrame * frame,GstSubtitleDecodedFrame * decoded_frame)109 static gboolean gst_subtitle_srt_parse_decode_frame(GstSubtitleBaseParse *base, const GstSubtitleFrame *frame,
110 GstSubtitleDecodedFrame *decoded_frame)
111 {
112 GstSubtitleSrtParse *parse = GST_SUBTITLE_SRT_PARSE_CAST(base);
113
114 g_return_val_if_fail((parse != nullptr) && (decoded_frame != nullptr), FALSE);
115
116 g_return_val_if_fail((frame->len != 0) && (frame->len <= MAX_BUFFER_SIZE), FALSE);
117
118 decoded_frame->data = static_cast<guint8 *>(g_malloc((gsize)frame->len + 1));
119 g_return_val_if_fail(decoded_frame->data != nullptr, FALSE);
120
121 if (memcpy_s(decoded_frame->data, frame->len + 1, frame->data, frame->len) != EOK) {
122 GST_WARNING_OBJECT(parse, "srt memory copy failed");
123 g_free(decoded_frame->data);
124 decoded_frame->data = nullptr;
125 return FALSE;
126 }
127
128 decoded_frame->data[frame->len] = 0;
129 decoded_frame->duration = base->state.duration;
130 decoded_frame->pts = base->state.start_time;
131 decoded_frame->timestamp = decoded_frame->pts;
132 decoded_frame->len = frame->len;
133 parse->last_end = static_cast<gint64>(decoded_frame->pts + decoded_frame->duration);
134
135 return TRUE;
136 }
137
srt_trailing_newlines(gchar * text,gsize len)138 static void srt_trailing_newlines(gchar *text, gsize len)
139 {
140 if (text != nullptr) {
141 while ((len > 1) && (text[len - 1] == '\n')) {
142 text[len - 1] = '\0';
143 --len;
144 }
145 }
146 }
147
srt_fix_up_markup_handle_slash_tag(const gchar ** next_tag,guint * num_open_tags,const gchar * open_tags)148 static void srt_fix_up_markup_handle_slash_tag(const gchar **next_tag, guint *num_open_tags, const gchar *open_tags)
149 {
150 ++(*next_tag);
151 g_return_if_fail((*num_open_tags != 0) && (open_tags[*num_open_tags - 1] == **next_tag));
152 /* it's all good, closing tag which is open */
153 --(*num_open_tags);
154 }
155
string_token(const gchar * string,const gchar * delimiter,gchar ** first)156 static const gchar *string_token(const gchar *string, const gchar *delimiter, gchar **first)
157 {
158 g_return_val_if_fail((string != nullptr) && (delimiter != nullptr) && (first != nullptr), nullptr);
159
160 const gchar *next = static_cast<const gchar *>(strstr(string, delimiter));
161 if (next != nullptr) {
162 *first = g_strndup(string, (gsize)(next - string));
163 } else {
164 *first = g_strdup(string);
165 }
166
167 return next;
168 }
169
srt_fix_up_markup_handle_f_tag(const gchar * next_tag)170 static gboolean srt_fix_up_markup_handle_f_tag(const gchar *next_tag)
171 {
172 gchar *name = nullptr;
173 gchar *attr_name = nullptr;
174 gchar *attr_value = nullptr;
175 gboolean ret = TRUE;
176
177 const gchar *next = string_token(next_tag, " ", &name);
178 if (next != nullptr) {
179 next = string_token(next + 1, "=", &attr_name);
180 }
181 if ((next != nullptr) && (string_token(next + 1, ">", &attr_value) == nullptr)) {
182 GST_WARNING("string_token failed");
183 }
184 if ((attr_name != nullptr) && (g_ascii_strcasecmp("color", attr_name) == 0)) {
185 if (attr_value != nullptr && string_token(attr_value + 1, "\"", &attr_value)) {
186 int32_t len = static_cast<int32_t>(strlen(attr_value));
187 if ((*attr_value != '#') || (len != COLOR_VALUE_LEN)) {
188 ret = FALSE;
189 goto BEACH;
190 }
191 }
192 } else {
193 ret = FALSE;
194 goto BEACH;
195 }
196 BEACH:
197 g_free(name);
198 g_free(attr_name);
199 g_free(attr_value);
200 return ret;
201 }
202
srt_remove_tag(gchar * des,const gchar * src)203 static gboolean srt_remove_tag(gchar *des, const gchar *src)
204 {
205 g_return_val_if_fail((des != nullptr) && (src != nullptr), FALSE);
206 g_return_val_if_fail(memmove_s(des, strlen(des) + 1, src, strlen(src) + 1) == EOK, FALSE);
207 return TRUE;
208 }
209
210 /* delete <...> */
srt_remove_tags(gchar * text)211 static void srt_remove_tags(gchar *text)
212 {
213 gchar *pos = nullptr;
214 gchar *gt = nullptr;
215 gboolean pos_bool = FALSE;
216
217 for (pos = text; (pos != nullptr) && (*pos != '\0'); pos += (pos_bool ? 0 : 1)) {
218 pos_bool = FALSE;
219 gt = strstr(pos + 1, ">");
220 if ((strncmp(pos, "<", (size_t)1) == 0) && (gt != nullptr)) {
221 if (srt_remove_tag(pos, gt + strlen(">"))) {
222 pos_bool = TRUE;
223 }
224 }
225 }
226 }
227
srt_fix_up_markup_handle(const gchar * next_tag,guint * num_open_tags,gchar ** text,gchar * open_tags,guint open_tags_len)228 static gboolean srt_fix_up_markup_handle(const gchar *next_tag, guint *num_open_tags,
229 gchar **text, gchar *open_tags, guint open_tags_len)
230 {
231 switch (*next_tag) {
232 case '/': {
233 srt_fix_up_markup_handle_slash_tag(&next_tag, num_open_tags, open_tags);
234 break;
235 }
236 case 'i':
237 case 'I':
238 case 'b':
239 case 'B':
240 case 'u':
241 case 'U':
242 case 's': {
243 if (*num_open_tags >= open_tags_len) {
244 /* something dodgy is going on, stop parsing */
245 return FALSE;
246 }
247 open_tags[*num_open_tags] = *next_tag;
248 ++(*num_open_tags);
249 break;
250 }
251 case 'f': {
252 if (*num_open_tags >= open_tags_len) {
253 /* something dodgy is going on, stop parsing */
254 return FALSE;
255 }
256 if (srt_fix_up_markup_handle_f_tag(next_tag) == FALSE) {
257 srt_remove_tags(*text);
258 return FALSE;
259 }
260 open_tags[*num_open_tags] = *next_tag;
261 ++(*num_open_tags);
262 break;
263 }
264 default: {
265 GST_ERROR("unexpected tag '%c' (%s)", *next_tag, next_tag);
266 g_free(*text);
267 (*text) = nullptr;
268 return FALSE;
269 }
270 }
271
272 return TRUE;
273 }
274
srt_fix_up_markup_add_tag(guint num_open_tags,const gchar * open_tags,const gchar * text)275 static GString *srt_fix_up_markup_add_tag(guint num_open_tags, const gchar *open_tags, const gchar *text)
276 {
277 GString *s = g_string_new(text);
278 g_return_val_if_fail(s != nullptr, nullptr);
279 if (s->str == nullptr) {
280 (void)g_string_free(s, (gboolean)TRUE);
281 return nullptr;
282 }
283
284 while ((num_open_tags > 0) && (num_open_tags < SRT_MAX_OPEN_TAGS_NUM)) {
285 GST_LOG("adding missing closing tag '%c'", open_tags[num_open_tags - 1]);
286 g_warn_if_fail(g_string_append_c(s, '<') != nullptr);
287 g_warn_if_fail(g_string_append_c(s, '/') != nullptr);
288 if ((open_tags[num_open_tags - 1] == 'f') || (open_tags[num_open_tags - 1] == 's')) {
289 g_warn_if_fail(g_string_append(s, "font") != nullptr);
290 } else {
291 g_warn_if_fail(g_string_append_c(s, open_tags[num_open_tags - 1]) != nullptr);
292 }
293 g_warn_if_fail(g_string_append_c(s, '>') != nullptr);
294 --num_open_tags;
295 }
296
297 return s;
298 }
299
300 /* if only have the start tag, add the end tag */
srt_fix_up_markup(gchar ** text)301 static void srt_fix_up_markup(gchar **text)
302 {
303 g_return_if_fail((text != nullptr) && (*text != nullptr));
304
305 gchar open_tags[SRT_MAX_OPEN_TAGS_NUM] = {0};
306 guint num_open_tags = 0;
307 gchar *cur = *text;
308
309 while ((*cur != '\0') && (num_open_tags < SRT_MAX_OPEN_TAGS_NUM)) {
310 gchar *next_tag = strchr(cur, '<');
311 if (next_tag == nullptr) {
312 break;
313 }
314 ++next_tag;
315 g_return_if_fail(srt_fix_up_markup_handle(next_tag, &num_open_tags,
316 text, open_tags, SRT_MAX_OPEN_TAGS_NUM) != FALSE);
317 cur = next_tag;
318 }
319
320 srt_remove_tags(*text);
321 g_return_if_fail(num_open_tags != 0);
322
323 GString *s = srt_fix_up_markup_add_tag(num_open_tags, open_tags, *text);
324 g_return_if_fail(s != nullptr);
325 g_free(*text);
326 *text = g_string_free(s, FALSE);
327 }
328
srt_parse_calibrate_time(gchar * srt_str,guint len)329 static void srt_parse_calibrate_time(gchar *srt_str, guint len)
330 {
331 /*
332 * convert ' ' to '0'
333 * or
334 * convert '.' to ','
335 * for example, "hh:mm:ss. 1 " is converted to "hh:mm:ss,010"
336 */
337 guint i;
338 for (i = 0; i < len; i++) {
339 if (srt_str[i] == ' ') {
340 srt_str[i] = '0';
341 } else if (srt_str[i] == '.') {
342 srt_str[i] = ',';
343 }
344 }
345 }
346
parse_timestamp(const gchar * srt_str,GstClockTime * timestamp)347 static gboolean parse_timestamp(const gchar *srt_str, GstClockTime *timestamp)
348 {
349 guint hour;
350 guint min;
351 guint sec;
352 guint msec;
353
354 if (sscanf_s(srt_str, "%u:%u:%u,%u", &hour, &min, &sec, &msec) != 4) { // 4 shows hour:min:sec,msec
355 GST_WARNING("failed to parse subrip timestamp string '%s'", srt_str);
356 return FALSE;
357 }
358 g_return_val_if_fail((hour <= 23) && // hour [0,23]
359 (min <= 59) && // min [0,59]
360 (sec <= 59) && // sec [0,59]
361 (msec <= 999), // msec [0,999]
362 FALSE);
363
364 guint64 hour_time = static_cast<guint64>(hour * 3600) * GST_SECOND; // 1 hour = 3600 secs
365 guint64 min_time = static_cast<guint64>(min * 60) * GST_SECOND; // 1 min = 60 secs
366 guint64 sec_time = static_cast<guint64>(sec) * GST_SECOND;
367 guint64 msec_time = static_cast<guint64>(msec) * GST_MSECOND;
368 *timestamp = hour_time + min_time + sec_time + msec_time;
369
370 return TRUE;
371 }
372
srt_parse_time(const gchar * ts_string,GstClockTime * timestamp)373 static gboolean srt_parse_time(const gchar *ts_string, GstClockTime *timestamp)
374 {
375 gchar srt_str[128] = {0}; // 128 shows the length of srt_str
376 while (*ts_string == ' ') {
377 ++ts_string;
378 }
379
380 g_return_val_if_fail(g_strlcpy(srt_str, ts_string, (gsize)sizeof(srt_str)) != 0, FALSE);
381 gchar *end = strstr(srt_str, "-->");
382 if (end != nullptr) {
383 *end = '\0';
384 }
385 g_return_val_if_fail(g_strchomp(srt_str) != nullptr, FALSE);
386 srt_parse_calibrate_time(srt_str, strlen(srt_str));
387
388 /* make sure we have exactly three digits after comma */
389 gchar *p = strchr(srt_str, ',');
390 g_return_val_if_fail(p != nullptr, FALSE);
391 ++p;
392 gsize len = static_cast<gsize>(strlen(p));
393 if (len > MSEC_CONTAIN_BITS) {
394 p[MSEC_CONTAIN_BITS] = '\0';
395 } else {
396 while (len < MSEC_CONTAIN_BITS) {
397 g_warn_if_fail(g_strlcat(&p[len], "0", 2) != 0); // 2 shows sizeof(&p[len])
398 ++len;
399 }
400 }
401
402 GST_LOG("srt parsing timestamp '%s'", srt_str);
403 return parse_timestamp(srt_str, timestamp);
404 }
405
parse_subsrt_time(GstSubtitleBaseParse * base,const gchar * line)406 static gchar *parse_subsrt_time(GstSubtitleBaseParse *base, const gchar *line)
407 {
408 GstSubtitleSrtParse *parse = GST_SUBTITLE_SRT_PARSE_CAST(base);
409 GstClockTime ts_start;
410 GstClockTime ts_end;
411 const gchar *end_time = nullptr;
412
413 /* looking for start_time --> end_time */
414 if (line != nullptr) {
415 end_time = static_cast<const gchar *>(strstr(line, " --> "));
416 }
417 if (end_time != nullptr) {
418 if (srt_parse_time(line, &ts_start) && srt_parse_time(end_time + strlen(" --> "), &ts_end) &&
419 (base->state.start_time <= ts_end)) {
420 parse->state = SUBTEXT_STATE;
421 base->state.start_time = ts_start;
422 base->state.duration = ts_end - ts_start;
423 } else {
424 parse->state = SUBNUM_STATE;
425 }
426 } else {
427 GST_DEBUG("error parsing subrip time line '%s'", line);
428 parse->state = SUBNUM_STATE;
429 }
430
431 return nullptr;
432 }
433
parse_subsrt_subtext(GstSubtitleBaseParse * base,const gchar * line)434 static gchar *parse_subsrt_subtext(GstSubtitleBaseParse *base, const gchar *line)
435 {
436 GstSubtitleSrtParse *parse = GST_SUBTITLE_SRT_PARSE_CAST(base);
437 gchar *ret = nullptr;
438 gchar *line_end = nullptr;
439 g_return_val_if_fail(line != nullptr && parse->buf != nullptr, nullptr);
440
441 line_end = const_cast<gchar *>(strchr(line, '\n'));
442 if (line_end != nullptr) {
443 if ((line[0] == '\r') && (line[1] == '\n')) {
444 *(line_end - 1) = '\0';
445 } else if (line[0] == '\n') {
446 *(line_end) = '\0';
447 }
448
449 if ((strlen(line) > 1) && (line_end != line) && (*(line_end - 1) == '\r')) {
450 *(line_end - 1) = '\n';
451 *line_end = '\0';
452 }
453 }
454
455 GST_DEBUG_OBJECT(parse, "parse->buf->len = %" G_GSIZE_FORMAT ", strlen(line) = %u",
456 parse->buf->len, (uint32_t)strlen(line));
457
458 g_warn_if_fail(g_string_append(parse->buf, line) != nullptr);
459
460 if (strlen(line) == 0) {
461 if (strlen(parse->buf->str)) {
462 ret = g_strdup(parse->buf->str);
463 g_return_val_if_fail(ret != nullptr, nullptr);
464 GST_DEBUG_OBJECT(parse, "g_strdup success");
465 } else {
466 ret = static_cast<gchar *>(g_malloc0(2)); // assign 2 bytes storage
467 g_return_val_if_fail(ret != nullptr, nullptr);
468 GST_DEBUG_OBJECT(parse, "g_malloc0 success");
469 ret[0] = 0;
470 }
471
472 g_warn_if_fail(g_string_truncate(parse->buf, 0) != nullptr);
473 parse->state = SUBNUM_STATE;
474
475 srt_trailing_newlines(ret, (gsize)strlen(ret));
476 srt_fix_up_markup(&ret);
477 return ret;
478 }
479
480 return nullptr;
481 }
482
parse_subsrt(GstSubtitleBaseParse * base,const gchar * line)483 static gchar *parse_subsrt(GstSubtitleBaseParse *base, const gchar *line)
484 {
485 g_return_val_if_fail((line != nullptr) && (base != nullptr), nullptr);
486
487 int subnum;
488 GstSubtitleSrtParse *parse = GST_SUBTITLE_SRT_PARSE_CAST(base);
489
490 switch (parse->state) {
491 case SUBNUM_STATE: {
492 /* looking for a single integer */
493 if (sscanf_s(line, "%d", &subnum) == 1) {
494 parse->state = TIME_STATE;
495 }
496 return nullptr;
497 }
498 case TIME_STATE: {
499 return parse_subsrt_time(base, line);
500 }
501 case SUBTEXT_STATE: {
502 return parse_subsrt_subtext(base, line);
503 }
504 default: {
505 g_return_val_if_reached(nullptr);
506 }
507 }
508 }
509
read_frame_from_external(GstSubtitleBaseParse * base,GstSubtitleFrame * frame)510 static gsize read_frame_from_external(GstSubtitleBaseParse *base, GstSubtitleFrame *frame)
511 {
512 GstSubtitleSrtParse *parse = GST_SUBTITLE_SRT_PARSE_CAST(base);
513 gchar *subtitle = nullptr;
514 gchar *line = nullptr;
515 g_return_val_if_fail(parse->buf != nullptr, 0);
516
517 gsize num = gst_subtitle_read_line(base, &line);
518 if (num == 0) {
519 g_return_val_if_fail(parse->buf->str != nullptr, 0);
520 if (base->recv_eos && (parse->state == SUBTEXT_STATE) && (strlen(parse->buf->str) != 0)) {
521 subtitle = g_strdup(parse->buf->str);
522 g_return_val_if_fail(subtitle != nullptr, 0);
523 g_warn_if_fail(g_string_truncate(parse->buf, 0) != nullptr);
524 parse->state = SUBNUM_STATE;
525
526 srt_trailing_newlines(subtitle, (gsize)strlen(subtitle));
527 srt_fix_up_markup(&subtitle);
528 if (subtitle != nullptr) {
529 frame->data = (guint8 *)subtitle;
530 frame->len = strlen(subtitle);
531 return frame->len;
532 } else {
533 frame->data = nullptr;
534 frame->len = 0;
535 }
536 }
537 return 0;
538 }
539
540 subtitle = parse_subsrt(base, line);
541 if (subtitle != nullptr) {
542 frame->data = (guint8 *)subtitle;
543 frame->len = strlen(subtitle);
544 } else {
545 frame->data = nullptr;
546 frame->len = 0;
547 }
548 g_free(line);
549
550 return num;
551 }
552
gst_subtitle_srt_parse_read_frame(GstSubtitleBaseParse * base,GstSubtitleFrame * frame)553 static gsize gst_subtitle_srt_parse_read_frame(GstSubtitleBaseParse *base, GstSubtitleFrame *frame)
554 {
555 g_return_val_if_fail((base != nullptr) && (frame != nullptr), 0);
556
557 gsize num = 0;
558
559 if (!base->from_internal) {
560 num = read_frame_from_external(base, frame);
561 }
562
563 return num;
564 }
565
gst_subtitle_srt_parse_get_src_caps(const GstSubtitleBaseParse * base,gint stream_id)566 static GstCaps *gst_subtitle_srt_parse_get_src_caps(const GstSubtitleBaseParse *base, gint stream_id)
567 {
568 GstCaps *caps = nullptr;
569 const gchar *language = "Unknown";
570 (void)stream_id;
571
572 caps = gst_caps_new_simple("text/x-raw", "format", G_TYPE_STRING, "pango-markup", nullptr);
573 g_return_val_if_fail(caps != nullptr && base != nullptr, nullptr);
574
575 if (base->language != nullptr) {
576 language = base->language;
577 }
578 gst_caps_set_simple(caps, "language", G_TYPE_STRING, language, "type", G_TYPE_STRING, "SrtSubtitle", nullptr);
579
580 return caps;
581 }
582
gst_subtitle_srt_parse_seek(GstSubtitleBaseParse * base,const GstEvent * event)583 static void gst_subtitle_srt_parse_seek(GstSubtitleBaseParse *base, const GstEvent *event)
584 {
585 GstSubtitleSrtParse *parse = GST_SUBTITLE_SRT_PARSE_CAST(base);
586 g_return_if_fail((parse != nullptr) && (event != nullptr));
587
588 GST_INFO_OBJECT(parse, "srt seek handling %s event", GST_EVENT_TYPE_NAME(event));
589 parse->state = SUBNUM_STATE;
590 base->state.start_time = 0;
591
592 if (parse->buf != nullptr) {
593 g_warn_if_fail(g_string_truncate(parse->buf, 0) != nullptr);
594 }
595
596 GST_INFO_OBJECT(parse, "srt seek out");
597 }
598
srt_parse_data_format_autodetect_regex_once(void)599 static GRegex *srt_parse_data_format_autodetect_regex_once(void)
600 {
601 GError *gerr = nullptr;
602 GRegex *result = g_regex_new("^[\\s\\n]{0,}[\\n]{0,1} {0,3}[ 0-9]{1,4}\\s{0,}(\x0d){0,1}\x0a"
603 " {0,1}[0-9]{1,2}: {0,1}[0-9]{1,2}: {0,1}[0-9]{1,2}[,.] {0,2}[0-9]{1,3}"
604 " +-{2}> +[0-9]{1,2}: {0,1}[0-9]{1,2}: {0,1}[0-9]{1,2}[,.] {0,2}[0-9]{1,3}",
605 (GRegexCompileFlags)(G_REGEX_RAW | G_REGEX_OPTIMIZE), (GRegexMatchFlags)0, &gerr);
606 if ((result == nullptr) && (gerr != nullptr)) {
607 GST_WARNING("compilation of srt regex failed: %s", gerr->message);
608 }
609 if (gerr != nullptr) {
610 g_error_free(gerr);
611 }
612 return result;
613 }
614
srt_format_detect(const gchar * match_str)615 static GstCaps *srt_format_detect(const gchar *match_str)
616 {
617 g_return_val_if_fail(match_str != nullptr, nullptr);
618
619 GstCaps *caps = nullptr;
620 GRegex *subrip_grx = nullptr;
621
622 subrip_grx = srt_parse_data_format_autodetect_regex_once();
623 if (g_regex_match(subrip_grx, match_str, (GRegexMatchFlags)0, nullptr)) {
624 caps = SRT_CAPS;
625 }
626 g_regex_unref(subrip_grx);
627 subrip_grx = nullptr;
628
629 if (caps == nullptr) {
630 GST_DEBUG("srt caps is nullptr");
631 }
632
633 return caps;
634 }
635
gst_subtitle_srt_parse_type_find(GstTypeFind * tf,gpointer priv)636 static void gst_subtitle_srt_parse_type_find(GstTypeFind *tf, gpointer priv)
637 {
638 g_return_if_fail(tf != nullptr);
639
640 GST_INFO("srt parse type find in");
641 gst_subtitle_typefind(tf, priv, srt_format_detect);
642 }
643
gst_subtitle_srt_parse_register(GstPlugin * plugin)644 gboolean gst_subtitle_srt_parse_register(GstPlugin *plugin)
645 {
646 GST_INFO_OBJECT(plugin, "srt parse register in");
647 g_return_val_if_fail(gst_type_find_register(plugin, "srt_typefind",
648 GST_RANK_PRIMARY + 1, gst_subtitle_srt_parse_type_find, "srt,txt", SRT_CAPS, nullptr, nullptr), FALSE);
649 g_return_val_if_fail(gst_element_register(plugin, "srt",
650 GST_RANK_PRIMARY + 1, GST_TYPE_SUBTITLE_SRT_PARSE), FALSE);
651 GST_INFO_OBJECT(plugin, "srt parse register out");
652 return TRUE;
653 }
654