• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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