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