• 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_base_parse_wrap.h"
17 #include <sys/time.h>
18 #include "securec.h"
19 #include "scope_guard.h"
20 #include "media_log.h"
21 #include "gst_subtitle_common.h"
22 
23 using namespace OHOS;
24 using namespace OHOS::Media;
25 
26 namespace {
27     constexpr gsize MAX_BUFFER_SIZE = 100000000;
28     constexpr guint64 SEC_TO_MSEC = 1000;
29     constexpr guint BOM_OF_UTF_8 = 3;
30     constexpr guint FIRST_INDEX_OF_UTF_8 = 0;
31     constexpr guint SECOND_INDEX_OF_UTF_8 = 1;
32     constexpr guint THIRD_INDEX_OF_UTF_8 = 2;
33 }
34 
src_event_seek_event_handle(GstSeekFlags * flags,gdouble * rate,GstSegment * seeksegment,GstSubtitleBaseParse * self,GstEvent * event)35 static gboolean src_event_seek_event_handle(GstSeekFlags *flags, gdouble *rate, GstSegment *seeksegment,
36     GstSubtitleBaseParse *self, GstEvent *event)
37 {
38     GstFormat format;
39     GstSeekType start_type;
40     GstSeekType stop_type;
41     gint64 start;
42     gint64 stop;
43     gboolean update = FALSE;
44 
45     gst_event_parse_seek(event, rate, &format, flags, &start_type, &start, &stop_type, &stop);
46     g_return_val_if_fail(format == GST_FORMAT_TIME, FALSE);
47 
48     if (self->segment != nullptr) {
49         gst_segment_copy_into((GstSegment *)self->segment, seeksegment);
50     }
51 
52     if (gst_segment_do_seek((GstSegment *)self->segment, *rate, format, *flags, start_type,
53         (guint64)start, stop_type, (guint64)stop, &update)) {
54         if ((*flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_AFTER) {
55             self->seek_snap_after = TRUE;
56         } else {
57             self->seek_snap_after = FALSE;
58         }
59         GST_INFO_OBJECT(self, "segment after seek: 0x%06" PRIXPTR ", seek_snap_after: %d",
60             FAKE_POINTER(&self->segment), self->seek_snap_after);
61 
62         if ((self->segment != nullptr) && (self->event_segment != nullptr)) {
63             gst_segment_copy_into((GstSegment *)self->segment, (GstSegment *)self->event_segment);
64         }
65     } else {
66         if (self->segment != nullptr) {
67             gst_segment_copy_into(seeksegment, (GstSegment *)self->segment);
68         }
69         GST_INFO_OBJECT(self, "segment fail after seek: 0x%06" PRIXPTR, FAKE_POINTER(&self->segment));
70     }
71 
72     return TRUE;
73 }
74 
src_event_seek_event(const GstSubtitleBaseParseClass * baseclass,GstSubtitleBaseParse * self,GstEvent * event)75 static gboolean src_event_seek_event(const GstSubtitleBaseParseClass *baseclass,
76     GstSubtitleBaseParse *self, GstEvent *event)
77 {
78     GstSeekFlags flags;
79     gdouble rate;
80     GstSegment seeksegment;
81 
82     g_return_val_if_fail(self->last_seekseq != event->seqnum, FALSE);
83     self->last_seekseq = event->seqnum;
84     if (baseclass->on_seek_pfn != nullptr) {
85         baseclass->on_seek_pfn(self, event);
86     }
87 
88     gboolean err_ret = src_event_seek_event_handle(&flags, &rate, &seeksegment, self, event);
89     g_return_val_if_fail(err_ret, FALSE);
90 
91     /* external subtitles push seek event upstream, internal subtitles push saved buffer when switched */
92     if (!self->from_internal) {
93         err_ret = gst_pad_push_event(self->sinkpad, gst_event_new_seek(rate, GST_FORMAT_BYTES, flags,
94             GST_SEEK_TYPE_SET, (gint64)0, GST_SEEK_TYPE_NONE, (gint64)0));
95         g_return_val_if_fail(!err_ret, TRUE);
96         GST_WARNING_OBJECT(self, "seek to 0 bytes failed");
97         if (self->segment != nullptr) {
98             gst_segment_copy_into(&seeksegment, (GstSegment *)self->segment);
99         }
100     }
101 
102     return TRUE;
103 }
104 
gst_subtitle_base_parse_get_stream_by_pad(const GstSubtitleBaseParse * self,const GstPad * pad)105 static GstSubtitleStream *gst_subtitle_base_parse_get_stream_by_pad(const GstSubtitleBaseParse *self,
106     const GstPad *pad)
107 {
108     GstSubtitleStream *stream = nullptr;
109     gint index = 0;
110 
111     while (index < self->stream_num) {
112         if (pad == self->streams[index]->pad) {
113             stream = self->streams[index];
114             break;
115         }
116         index++;
117     }
118 
119     return stream;
120 }
121 
gst_subtitle_base_parse_switch_stream(GstSubtitleBaseParse * self,const GstPad * pad,gboolean active)122 static void gst_subtitle_base_parse_switch_stream(GstSubtitleBaseParse *self, const GstPad *pad, gboolean active)
123 {
124     g_return_if_fail(self != nullptr && pad != nullptr);
125 
126     GstSubtitleBaseParseClass *baseclass = GST_SUBTITLE_BASE_PARSE_GET_CLASS(self);
127     g_return_if_fail(baseclass != nullptr);
128 
129     GstSubtitleStream *stream = gst_subtitle_base_parse_get_stream_by_pad(self, pad);
130     g_return_if_fail(stream != nullptr);
131 
132     stream->active = active;
133     GST_INFO_OBJECT(self, "stream->stream_id: %d, active: %d", stream->stream_id, active);
134 }
135 
gst_subtitle_base_parse_src_event(GstPad * pad,GstObject * parent,GstEvent * event)136 static gboolean gst_subtitle_base_parse_src_event(GstPad *pad, GstObject *parent, GstEvent *event)
137 {
138     g_return_val_if_fail((pad != nullptr) && (parent != nullptr) && (event != nullptr), FALSE);
139 
140     GstSubtitleBaseParse *self = static_cast<GstSubtitleBaseParse *>((void *)parent);
141     GstSubtitleBaseParseClass *baseclass = GST_SUBTITLE_BASE_PARSE_GET_CLASS(self);
142     ON_SCOPE_EXIT(0) {
143         gst_event_unref(event);
144     };
145     g_return_val_if_fail(baseclass != nullptr, FALSE);
146     gboolean ret = FALSE;
147 
148     GST_INFO_OBJECT(self, "Handling %s event", GST_EVENT_TYPE_NAME(event));
149     switch (GST_EVENT_TYPE(event)) {
150         case GST_EVENT_SEEK: {
151             ret = src_event_seek_event(baseclass, self, event);
152             gst_event_unref(event);
153             event = nullptr;
154             break;
155         }
156         case GST_EVENT_CUSTOM_UPSTREAM: {
157             gboolean active = FALSE;
158 
159             const GstStructure *structure = gst_event_get_structure(event);
160             if (structure == nullptr || gst_structure_get_name(structure) == nullptr) {
161                 gst_event_unref(event);
162                 break;
163             }
164 
165             if (strcmp(gst_structure_get_name(structure), "select-stream") == 0) {
166                 if (!gst_structure_get_boolean(structure, "activity", &active)) {
167                     active = TRUE;
168                 }
169                 gst_subtitle_base_parse_switch_stream(self, pad, active);
170                 self->switching = TRUE;
171                 return TRUE;
172             } else {
173                 ret = gst_pad_event_default(pad, parent, event);
174             }
175             break;
176         }
177         default: {
178             ret = gst_pad_event_default(pad, parent, event);
179             break;
180         }
181     }
182     CANCEL_SCOPE_EXIT_GUARD(0);
183     return ret;
184 }
185 
186 /* push gstbuffer to srcpad */
gst_subtitle_base_push_data(GstSubtitleBaseParse * self,GstPad * pad,GstBuffer * buffer)187 static GstFlowReturn gst_subtitle_base_push_data(GstSubtitleBaseParse *self, GstPad *pad, GstBuffer *buffer)
188 {
189     GstFlowReturn ret = GST_FLOW_OK;
190     guint64 clip_start = 0;
191     guint64 clip_end = 0;
192     gboolean in_segment = true;
193 
194     g_return_val_if_fail(buffer != nullptr, ret);
195     ON_SCOPE_EXIT(0) {
196         GST_WARNING_OBJECT(self, "Construct GstBuffer failed");
197         gst_buffer_unref(buffer);
198         buffer = nullptr;
199     };
200     g_return_val_if_fail(self != nullptr && pad != nullptr, ret);
201     CANCEL_SCOPE_EXIT_GUARD(0);
202 
203     /*
204      * Determine whether the display time interval of the buffer has an intersection with the segment.
205      * If has an intersection, adjust the pts and duration of the buffer to the range of the segment.
206      */
207     if (self->seek_snap_after) {
208         guint64 start_time = GST_BUFFER_PTS(buffer);
209         guint64 end_time = start_time + GST_BUFFER_DURATION(buffer);
210         in_segment = gst_segment_clip((GstSegment *)self->segment, GST_FORMAT_TIME, start_time, end_time,
211             &clip_start, &clip_end);
212         GST_BUFFER_PTS(buffer) = clip_start;
213         GST_BUFFER_DURATION(buffer) = clip_end - clip_start;
214     }
215 
216     if (in_segment && GST_IS_PAD(pad) && GST_PAD_IS_SRC(pad)) {
217         ret = gst_pad_push(pad, buffer);
218         if (G_LIKELY(ret != GST_FLOW_OK)) {
219             GST_ERROR_OBJECT(self, "Push subtitle buffer failed, ret = %d", ret);
220         } else {
221             GST_INFO_OBJECT(self, "Push subtitle buffer success, pts = %" GST_TIME_FORMAT
222                 ", duration = %" GST_TIME_FORMAT, GST_TIME_ARGS(GST_BUFFER_PTS(buffer)),
223                 GST_TIME_ARGS(GST_BUFFER_DURATION(buffer)));
224         }
225     } else {
226         GST_WARNING_OBJECT(self, "gst_subtitle_base_push_data:invalid time");
227         gst_buffer_unref(buffer);
228     }
229     return ret;
230 }
231 
handle_text_subtitle(GstSubtitleBaseParse * self,const GstSubtitleDecodedFrame * decoded_frame,GstSubtitleStream * stream,GstFlowReturn * ret)232 gboolean handle_text_subtitle(GstSubtitleBaseParse *self, const GstSubtitleDecodedFrame *decoded_frame,
233     GstSubtitleStream *stream, GstFlowReturn *ret)
234 {
235     g_return_val_if_fail((self != nullptr) && (decoded_frame != nullptr)
236         && (stream != nullptr) && (ret != nullptr), FALSE);
237     g_return_val_if_fail(decoded_frame->len <= MAX_BUFFER_SIZE, FALSE);
238 
239     GstPad *pad = stream->pad;
240     GstBuffer *buffer = gst_buffer_new_allocate(nullptr, (guint32)decoded_frame->len, nullptr);
241     g_return_val_if_fail(buffer != nullptr, FALSE);
242     (void)gst_buffer_fill(buffer, 0, decoded_frame->data, (guint32)decoded_frame->len);
243     GST_BUFFER_PTS(buffer) = decoded_frame->pts;
244     GST_BUFFER_DURATION(buffer) = decoded_frame->duration;
245 
246     if (!self->from_internal) {
247         *ret = gst_subtitle_base_push_data(self, pad, buffer);
248     }
249 
250     return TRUE;
251 }
252 
gst_subtitle_base_get_current_position(GstSubtitleBaseParse * self)253 static guint64 gst_subtitle_base_get_current_position(GstSubtitleBaseParse *self)
254 {
255     g_return_val_if_fail(self != nullptr, 0);
256 
257     GstFormat fmt;
258     guint64 position = GST_CLOCK_TIME_NONE;
259     GstBin *parent_bin = nullptr;
260 
261     GstQuery *query = gst_query_new_position(GST_FORMAT_TIME);
262     g_return_val_if_fail(query != nullptr, GST_CLOCK_TIME_NONE);
263     GstBin *tmp_bin = static_cast<GstBin *>((void *)gst_element_get_parent(self));
264     while (tmp_bin != nullptr) {
265         parent_bin = tmp_bin;
266         tmp_bin = static_cast<GstBin *>((void *)gst_element_get_parent(parent_bin));
267         if (tmp_bin == nullptr) {
268             break;
269         }
270         gst_object_unref(parent_bin);
271         parent_bin = nullptr;
272     }
273 
274     ON_SCOPE_EXIT(0) {
275         gst_query_unref(query);
276     };
277     g_return_val_if_fail(parent_bin != nullptr, GST_CLOCK_TIME_NONE);
278 
279     gboolean ret = gst_element_query(static_cast<GstElement *>((void *)parent_bin), query);
280     if (ret) {
281         gst_query_parse_position(query, &fmt, (gint64 *)(&position));
282     }
283 
284     gst_query_unref(query);
285     query = nullptr;
286     gst_object_unref(parent_bin);
287     return (static_cast<gint64>(position) >= 0) ? position : GST_CLOCK_TIME_NONE;
288 }
289 
gst_subtitle_get_stream_by_id(const GstSubtitleBaseParse * self,gint stream_id)290 GstSubtitleStream *gst_subtitle_get_stream_by_id(const GstSubtitleBaseParse *self, gint stream_id)
291 {
292     g_return_val_if_fail(self != nullptr, nullptr);
293 
294     GstSubtitleStream *stream = nullptr;
295     gint index = 0;
296 
297     while (index < self->stream_num) {
298         if (stream_id == self->streams[index]->stream_id) {
299             stream = self->streams[index];
300             break;
301         }
302         index++;
303     }
304 
305     return stream;
306 }
307 
free_subinfos_and_streams(GstSubtitleBaseParse * base_parse)308 void free_subinfos_and_streams(GstSubtitleBaseParse *base_parse)
309 {
310     gint index;
311     g_return_if_fail(base_parse != nullptr);
312 
313     for (index = 0; index < base_parse->stream_num; index++) {
314         if (base_parse->subinfos[index] != nullptr) {
315             g_free(base_parse->subinfos[index]->desc);
316             base_parse->subinfos[index]->desc = nullptr;
317             g_free(base_parse->subinfos[index]);
318             base_parse->subinfos[index] = nullptr;
319         }
320 
321         if (base_parse->streams[index] != nullptr) {
322             gst_caps_unref(base_parse->streams[index]->caps);
323             base_parse->streams[index]->caps = nullptr;
324 
325             gst_tag_list_unref(base_parse->streams[index]->tags);
326             base_parse->streams[index]->tags = nullptr;
327 
328             g_free(base_parse->streams[index]);
329             base_parse->streams[index] = nullptr;
330         }
331     }
332 }
333 
detect_sub_type_parse_extradata(const GstStructure * structure,GstSubtitleBaseParse * self)334 static void detect_sub_type_parse_extradata(const GstStructure *structure, GstSubtitleBaseParse *self)
335 {
336     if (gst_structure_get_int(structure, "stream_id", &self->stream_id)) {
337         GST_DEBUG_OBJECT(self, "stream_id:%d", self->stream_id);
338     }
339 }
340 
341 /*
342  * Determine whether subtitles are internal or external based on sinkpad caps.
343  * TRUE:  determine subtitle type(default: external)
344  * FALSE: can not query caps of peer pad
345  */
gst_subtitle_base_parse_detect_sub_type(GstSubtitleBaseParse * self,GstPad * sinkpad)346 static gboolean gst_subtitle_base_parse_detect_sub_type(GstSubtitleBaseParse *self, GstPad *sinkpad)
347 {
348     gboolean internal = FALSE;
349     gboolean ret = FALSE;
350 
351     g_return_val_if_fail((self != nullptr) && (sinkpad != nullptr), ret);
352 
353     GstPad *peer = gst_pad_get_peer(sinkpad);
354     g_return_val_if_fail(G_LIKELY(peer != nullptr), ret);
355     GstCaps *caps = gst_pad_query_caps(peer, nullptr);
356     g_object_unref(peer);
357     peer = nullptr;
358     g_return_val_if_fail(G_LIKELY(caps != nullptr), ret);
359 
360     GstStructure *structure = gst_caps_get_structure(caps, 0);
361     ON_SCOPE_EXIT(0) {
362         gst_caps_unref(caps);
363     };
364     g_return_val_if_fail(G_LIKELY(structure != nullptr), TRUE);
365 
366     if (!gst_structure_get_boolean(structure, "parsed", &internal)) {
367         self->from_internal = FALSE;
368     } else {
369         self->from_internal = internal;
370     }
371 
372     const gchar *language = gst_structure_get_string(structure, "language");
373     if (language != nullptr) {
374         g_free(self->language);
375         self->language = gst_subtitle_str_dup(language, FALSE, 0);
376     }
377 
378     /* extradata parsing */
379     detect_sub_type_parse_extradata(structure, self);
380 
381     return TRUE;
382 }
383 
handle_first_frame(GstPad * sinkpad,GstBuffer * buf,GstSubtitleBaseParse * self)384 gboolean handle_first_frame(GstPad *sinkpad, GstBuffer *buf, GstSubtitleBaseParse *self)
385 {
386     g_return_val_if_fail(sinkpad != nullptr && buf != nullptr && self != nullptr, FALSE);
387 
388     if (self->first_buffer) {
389         g_return_val_if_fail(gst_subtitle_base_parse_detect_sub_type(self, sinkpad), FALSE);
390         self->first_buffer = FALSE;
391     }
392 
393     return TRUE;
394 }
395 
check_utf8_encoding(GstSubtitleBaseParse * self,const gchar * str,gsize len)396 static gchar *check_utf8_encoding(GstSubtitleBaseParse *self, const gchar *str, gsize len)
397 {
398     g_return_val_if_fail(str != nullptr && len > 0, nullptr);
399 
400     /* check if it's utf-8 */
401     if (g_utf8_validate(str, (gint)len, nullptr)) {
402         if ((len >= BOM_OF_UTF_8) && ((guint8)str[FIRST_INDEX_OF_UTF_8] == 0xEF) &&
403             ((guint8)str[SECOND_INDEX_OF_UTF_8] == 0xBB) && ((guint8)str[THIRD_INDEX_OF_UTF_8] == 0xBF)) {
404                 str += BOM_OF_UTF_8;
405                 len -= BOM_OF_UTF_8;
406         }
407         return gst_subtitle_str_dup(str, TRUE, len);
408     }
409 
410     GST_INFO_OBJECT(self, "invalid utf-8!");
411     return gst_subtitle_str_dup(str, TRUE, len);
412 }
413 
fill_buffer_from_adapter(GstSubtitleBaseParse * base_parse,GstSubtitleBufferContext * buf_ctx)414 static void fill_buffer_from_adapter(GstSubtitleBaseParse *base_parse, GstSubtitleBufferContext *buf_ctx)
415 {
416     gsize avail = gst_adapter_available(buf_ctx->adapter);
417     gconstpointer data = gst_adapter_map(buf_ctx->adapter, avail);
418 
419     gchar *input = check_utf8_encoding(base_parse, (const gchar *)data, avail);
420 
421     gst_adapter_unmap(buf_ctx->adapter);
422     if ((input != nullptr) && (buf_ctx->text != nullptr)) {
423         buf_ctx->text = g_string_append(buf_ctx->text, input);
424         gst_adapter_flush(buf_ctx->adapter, avail);
425         GST_DEBUG_OBJECT(base_parse, "flush adapter size = %" G_GSIZE_FORMAT "", avail);
426     }
427     g_free(input);
428 }
429 
430 /* write @buffer to buffer */
gst_subtitle_base_parse_fill_buffer(GstSubtitleBaseParse * base_parse,GstBuffer * buffer)431 static void gst_subtitle_base_parse_fill_buffer(GstSubtitleBaseParse *base_parse, GstBuffer *buffer)
432 {
433     g_return_if_fail(base_parse != nullptr);
434 
435     GstSubtitleBufferContext *buf_ctx = &base_parse->buffer_ctx;
436     GstSubtitleBaseParseClass *kclass = GST_SUBTITLE_BASE_PARSE_GET_CLASS(base_parse);
437     g_return_if_fail(kclass != nullptr);
438 
439     g_mutex_lock(&base_parse->buffermutex);
440     /*
441      * If it is external subtitle, store @buffer in GstAdapter,
442      * then read as many strings as possible from the GstAdapter.
443      */
444     if (!base_parse->from_internal && buf_ctx->adapter != nullptr) {
445         gst_adapter_push(buf_ctx->adapter, buffer);
446         fill_buffer_from_adapter(base_parse, buf_ctx);
447     }
448     g_mutex_unlock(&base_parse->buffermutex);
449 }
450 
get_stream_new_queue(GstSubtitleBaseParse * base_parse,GstSubtitleStream * stream)451 static void get_stream_new_queue(GstSubtitleBaseParse *base_parse, GstSubtitleStream *stream)
452 {
453     if (!base_parse->from_internal) {
454         stream->last_result = GST_FLOW_OK;
455     }
456 }
457 
gst_subtitle_get_stream_handle(GstSubtitleBaseParse * base_parse,const GstSubtitleInfo * info,GstPad * srcpad,GstCaps * caps)458 static GstSubtitleStream *gst_subtitle_get_stream_handle(GstSubtitleBaseParse *base_parse,
459     const GstSubtitleInfo *info, GstPad *srcpad, GstCaps *caps)
460 {
461     const gchar *language = nullptr;
462     const gchar *format = nullptr;
463     gboolean activity = FALSE;
464     GstSubtitleStream *stream = static_cast<GstSubtitleStream *>(g_malloc0(sizeof(GstSubtitleStream)));
465     g_return_val_if_fail(stream != nullptr, nullptr);
466 
467     stream->stream_id = info->stream_id;
468     stream->pad = srcpad;
469     stream->caps = gst_caps_ref(caps);
470     GstStructure *caps_structure = gst_caps_get_structure(caps, 0);
471     if (caps_structure != nullptr) {
472         language = gst_structure_get_string(caps_structure, "language");
473         format = gst_structure_get_string(caps_structure, "type");
474     }
475 
476     if (info->stream_id == base_parse->stream_id) {
477         activity = TRUE;
478     }
479 
480     stream->tags = gst_tag_list_new(GST_TAG_LANGUAGE_CODE, language, GST_TAG_SUBTITLE_FORMAT, format,
481         GST_TAG_SUBTITLE_STREAM_ACTIVITY_FLAG, activity ? 1 : 0, GST_TAG_SUBTITLE_TYPE,
482         base_parse->from_internal, nullptr);
483     GST_DEBUG_OBJECT(base_parse, "subtitle stream: %d, language: %s, format: %s, internal: %d",
484         stream->stream_id, language, format, base_parse->from_internal);
485 
486     if (!gst_element_add_pad(GST_ELEMENT(base_parse), srcpad)) {
487         GST_ERROR_OBJECT(base_parse, "gst_element_add_pad failed");
488         g_object_unref(srcpad);
489         gst_tag_list_unref(stream->tags);
490         g_free(stream);
491         return nullptr;
492     }
493 
494     get_stream_new_queue(base_parse, stream);
495 
496     return stream;
497 }
498 
get_stream_srcpad_set(GstSubtitleBaseParse * base_parse,GstPad * srcpad)499 static void get_stream_srcpad_set(GstSubtitleBaseParse *base_parse, GstPad *srcpad)
500 {
501     gst_pad_set_event_function(srcpad, gst_subtitle_base_parse_src_event);
502     base_parse->pad_num++;
503 
504     GST_DEBUG_OBJECT(base_parse, "adding src pad %s", GST_PAD_NAME(srcpad));
505     gst_pad_use_fixed_caps(srcpad);
506     g_return_if_fail(gst_pad_set_active(srcpad, (gboolean)TRUE));
507 }
508 
gst_subtitle_get_stream(GstSubtitleBaseParse * base_parse,const GstSubtitleInfo * info)509 GstSubtitleStream *gst_subtitle_get_stream(GstSubtitleBaseParse *base_parse, const GstSubtitleInfo *info)
510 {
511     g_return_val_if_fail((base_parse != nullptr) && (info != nullptr), nullptr);
512 
513     GstCaps *caps = nullptr;
514     GstSubtitleBaseParseClass *baseclass = GST_SUBTITLE_BASE_PARSE_GET_CLASS(base_parse);
515     g_return_val_if_fail((baseclass != nullptr) && (baseclass->get_srcpad_caps_pfn != nullptr), nullptr);
516 
517     GString *padname = g_string_new(nullptr);
518     g_return_val_if_fail(padname != nullptr, nullptr);
519     ON_SCOPE_EXIT(0) {
520         (void)g_string_free(padname, (gboolean)TRUE);
521     };
522     g_return_val_if_fail(padname->str != nullptr, nullptr);
523     GstPadTemplate *src_pad_template = base_parse->srcpadtmpl;
524     g_return_val_if_fail(src_pad_template != nullptr, nullptr);
525     g_string_append_printf(padname, "src_%d", base_parse->pad_num);
526 
527     GstPad *srcpad = gst_pad_new_from_template(src_pad_template, padname->str);
528 
529     padname = nullptr;
530     g_return_val_if_fail(srcpad != nullptr, nullptr);
531     get_stream_srcpad_set(base_parse, srcpad);
532 
533     caps = baseclass->get_srcpad_caps_pfn(base_parse, info->stream_id);
534     g_return_val_if_fail(caps != nullptr, nullptr);
535     ON_SCOPE_EXIT(1) {
536         gst_caps_unref(caps);
537     };
538     g_return_val_if_fail(gst_pad_set_caps(srcpad, caps), nullptr);
539     return gst_subtitle_get_stream_handle(base_parse, info, srcpad, caps);
540 }
541 
get_subtitle_streams(const GstSubtitleBaseParseClass * baseclass,GstBuffer * buf,GstSubtitleBaseParse * self)542 gboolean get_subtitle_streams(const GstSubtitleBaseParseClass *baseclass,
543     GstBuffer *buf, GstSubtitleBaseParse *self)
544 {
545     g_return_val_if_fail((baseclass != nullptr) && (buf != nullptr) && (self != nullptr), FALSE);
546 
547     gst_subtitle_base_parse_fill_buffer(self, buf);
548 
549     if (!self->got_streams) {
550         if (self->subinfos[0] != nullptr) {
551             g_free(self->subinfos[0]);
552         }
553 
554         self->subinfos[0] = static_cast<GstSubtitleInfo *>(g_malloc0(sizeof(GstSubtitleInfo)));
555         if (self->subinfos[0] != nullptr) {
556             if (!self->from_internal) {
557                 self->stream_id = 0;
558             }
559             self->subinfos[0]->stream_id = self->stream_id;
560             self->streams[0] = gst_subtitle_get_stream(self, self->subinfos[0]);
561             if (self->streams[0] == nullptr) {
562                 g_free(self->subinfos[0]);
563                 self->subinfos[0] = nullptr;
564             } else {
565                 self->stream_num++;
566             }
567         } else {
568             GST_WARNING_OBJECT(self, "alloc substream info failed");
569         }
570         GST_INFO_OBJECT(self, "%s get subtitle streams success, stream_num %d",
571             GST_ELEMENT_NAME(self), self->stream_num);
572         self->got_streams = TRUE;
573     }
574 
575     return TRUE;
576 }
577 
gst_subtitle_push_stream_start_event(GstSubtitleBaseParse * base_parse)578 void gst_subtitle_push_stream_start_event(GstSubtitleBaseParse *base_parse)
579 {
580     g_return_if_fail(base_parse != nullptr);
581     g_return_if_fail(base_parse->stream_num != 0);
582 
583     gint i;
584     for (i = 0; i < base_parse->stream_num; i++) {
585         g_return_if_fail(base_parse->streams[i] != nullptr);
586 
587         gchar *stream_name = g_strdup_printf("%s_stream%d", GST_ELEMENT_NAME(base_parse), i);
588         GstEvent *event = gst_event_new_stream_start(stream_name);
589         ON_SCOPE_EXIT(0) {
590             g_free(stream_name);
591         };
592         g_return_if_fail(event != nullptr);
593         CANCEL_SCOPE_EXIT_GUARD(0);
594         const gchar *event_name = gst_event_type_get_name(GST_EVENT_TYPE(event));
595         GST_DEBUG_OBJECT(base_parse, "pushing event %s on pad %s",
596             event_name, GST_PAD_NAME(base_parse->streams[i]->pad));
597 
598         (void)gst_pad_push_event(base_parse->streams[i]->pad, event);
599         g_free(stream_name);
600     }
601 }
602 
gst_subtitle_set_caps(GstSubtitleBaseParse * base_parse)603 gboolean gst_subtitle_set_caps(GstSubtitleBaseParse *base_parse)
604 {
605     g_return_val_if_fail(base_parse != nullptr, FALSE);
606 
607     gint i;
608     if (base_parse->stream_num == 0) {
609         return FALSE;
610     }
611     for (i = 0; i < base_parse->stream_num; i++) {
612         g_return_val_if_fail(base_parse->streams[i] != nullptr &&
613             gst_pad_set_caps(base_parse->streams[i]->pad, base_parse->streams[i]->caps), FALSE);
614         GST_INFO_OBJECT(base_parse, "set caps on pad %s success", GST_PAD_NAME(base_parse->streams[i]->pad));
615     }
616 
617     return TRUE;
618 }
619 
gst_subtitle_set_tags(GstSubtitleBaseParse * base_parse)620 gboolean gst_subtitle_set_tags(GstSubtitleBaseParse *base_parse)
621 {
622     g_return_val_if_fail(base_parse != nullptr, FALSE);
623 
624     gint i;
625 
626     g_return_val_if_fail(base_parse->stream_num != 0, FALSE);
627     for (i = 0; i < base_parse->stream_num; i++) {
628         g_return_val_if_fail(base_parse->streams[i] != nullptr, FALSE);
629         GstEvent *event = gst_event_new_tag(gst_tag_list_ref(base_parse->streams[i]->tags));
630         ON_SCOPE_EXIT(0) {
631             gst_tag_list_unref(base_parse->streams[i]->tags);
632         };
633         g_return_val_if_fail(event != nullptr, FALSE);
634         GST_DEBUG_OBJECT(base_parse, "new event tag for streams[%d]", i);
635         const gchar *event_name = gst_event_type_get_name(GST_EVENT_TYPE(event));
636         GST_DEBUG_OBJECT(base_parse, "pushing taglist 0x%06" PRIXPTR " on pad %s",
637             FAKE_POINTER(base_parse->streams[i]->tags), GST_PAD_NAME(base_parse->streams[i]->pad));
638         g_return_val_if_fail(gst_pad_push_event(base_parse->streams[i]->pad, event), FALSE);
639         GST_INFO_OBJECT(base_parse, "pad %s send event %s success", GST_PAD_NAME(base_parse->streams[i]->pad),
640             event_name);
641         CANCEL_SCOPE_EXIT_GUARD(0);
642         gst_tag_list_unref(base_parse->streams[i]->tags);
643     }
644     return TRUE;
645 }
646 
chain_set_caps_and_tags(GstSubtitleBaseParse * self)647 gboolean chain_set_caps_and_tags(GstSubtitleBaseParse *self)
648 {
649     g_return_val_if_fail(self != nullptr, FALSE);
650 
651     gboolean need_tags = FALSE;
652 
653     if (G_UNLIKELY(self->need_srcpad_caps)) {
654         g_return_val_if_fail(gst_subtitle_set_caps(self), FALSE);
655         self->need_srcpad_caps = FALSE;
656         need_tags = TRUE;
657     }
658 
659     if (G_UNLIKELY(need_tags)) {
660         g_return_val_if_fail(gst_subtitle_set_tags(self), TRUE);
661         GST_INFO_OBJECT(self, "subtitle set taglist success");
662     }
663 
664     return TRUE;
665 }
666 
gst_subtitle_send_event(GstSubtitleBaseParse * base_parse,GstEvent * event)667 static gboolean gst_subtitle_send_event(GstSubtitleBaseParse *base_parse, GstEvent *event)
668 {
669     g_return_val_if_fail((base_parse != nullptr) && (event != nullptr), FALSE);
670 
671     gint i;
672     gboolean ret = TRUE;
673 
674     for (i = 0; i < base_parse->stream_num; i++) {
675         if (base_parse->streams[i] == nullptr) {
676             GST_ERROR_OBJECT(base_parse, "streams[%d] is nullptr", i);
677             ret = FALSE;
678         } else {
679             if (!gst_pad_push_event(base_parse->streams[i]->pad, gst_event_ref(event))) {
680                 GST_ERROR_OBJECT(base_parse, "pad %s push event failed", GST_PAD_NAME(base_parse->streams[i]->pad));
681                 ret = FALSE;
682             } else {
683                 GST_INFO_OBJECT(base_parse, "push event %s on pad %s success",
684                     gst_event_type_get_name(GST_EVENT_TYPE(event)), GST_PAD_NAME(base_parse->streams[i]->pad));
685             }
686         }
687     }
688 
689     gst_event_unref(event);
690     return ret;
691 }
692 
chain_push_new_segment_event(GstFlowReturn ret,GstSubtitleBaseParse * self)693 gboolean chain_push_new_segment_event(GstFlowReturn ret, GstSubtitleBaseParse *self)
694 {
695     g_return_val_if_fail(self != nullptr, FALSE);
696 
697     g_mutex_lock(&self->segmentmutex);
698     ON_SCOPE_EXIT(0) {
699         g_mutex_unlock(&self->segmentmutex);
700     };
701     gboolean result = TRUE;
702     if (G_UNLIKELY(self->need_segment)) {
703         GST_INFO_OBJECT(self, "begin pushing newsegment event with 0x%06" PRIXPTR, FAKE_POINTER(&self->segment));
704 
705         if (self->first_segment) {
706             self->event_segment->start = gst_subtitle_base_get_current_position(self);
707             GST_WARNING_OBJECT(self, "first segment, segment start set cur position = %" GST_TIME_FORMAT,
708                 GST_TIME_ARGS(self->event_segment->start));
709             self->first_segment = FALSE;
710         }
711         GstEvent *event = gst_event_new_segment((GstSegment *)self->event_segment);
712         g_return_val_if_fail(event != nullptr, FALSE);
713         if (gst_subtitle_send_event(self, gst_event_ref(event))) {
714             GST_INFO_OBJECT(self, "end pushing newsegment event with 0x%06" PRIXPTR, FAKE_POINTER(&self->segment));
715             self->need_segment = FALSE;
716         } else {
717             result = FALSE;
718             GST_WARNING_OBJECT(self, "push segment failed: %d", ret);
719         }
720         gst_event_unref(event);
721         event = nullptr;
722     }
723     return result;
724 }
725 
decode_one_frame(const GstSubtitleBaseParseClass * baseclass,GstSubtitleBaseParse * self,GstSubtitleFrame * frame,GstSubtitleDecodedFrame * decoded_frame)726 gboolean decode_one_frame(const GstSubtitleBaseParseClass *baseclass, GstSubtitleBaseParse *self,
727     GstSubtitleFrame *frame, GstSubtitleDecodedFrame *decoded_frame)
728 {
729     g_return_val_if_fail((baseclass != nullptr) && (baseclass->decode_frame_pfn != nullptr) &&
730         (self != nullptr) && (frame != nullptr), FALSE);
731     if ((frame->data == nullptr) || (decoded_frame == nullptr)) {
732         return FALSE;
733     }
734 
735     struct timeval decode_start;
736     struct timeval decode_end;
737 
738     (void)gettimeofday(&decode_start, nullptr);
739     gboolean got_frame = baseclass->decode_frame_pfn(self, frame, decoded_frame);
740 
741     g_free(frame->data);
742     frame->data = nullptr;
743 
744     ON_SCOPE_EXIT(0) {
745         gst_subtitle_free_frame(self, decoded_frame);
746     };
747     g_return_val_if_fail(G_LIKELY(got_frame), FALSE);
748     g_return_val_if_fail((decoded_frame->pts / GST_SECOND) <= G_MAXLONG, FALSE);
749     (void)gettimeofday(&decode_end, nullptr);
750 
751     GST_DEBUG_OBJECT(self, "decode subtitle frame use time %" G_GUINT64_FORMAT " us",
752         (guint64)(decode_end.tv_sec - decode_start.tv_sec) * SEC_TO_MSEC +
753         (guint64)(decode_end.tv_usec - decode_start.tv_usec));
754     CANCEL_SCOPE_EXIT_GUARD(0);
755     return TRUE;
756 }
757