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.h"
17 #include <cstdio>
18 #include <cstdlib>
19 #include <cstring>
20 #include <sys/types.h>
21 #include <sys/time.h>
22 #include "securec.h"
23 #include "glib.h"
24 #include "media_log.h"
25 #include "gst_subtitle_common.h"
26 #include "gst_subtitle_base_parse_wrap.h"
27
28 GST_DEBUG_CATEGORY_STATIC(gst_subtitle_base_parse_debug_category);
29 #define GST_CAT_DEFAULT gst_subtitle_base_parse_debug_category
30
31 static GstElementClass *g_parentClass = nullptr;
32
33 static void gst_subtitle_base_parse_class_init(GstSubtitleBaseParseClass *klass);
34 static void gst_subtitle_base_parse_init(GstSubtitleBaseParse *base_parse, gpointer gst_class);
35 static void gst_subtitle_base_parse_dispose(GObject *object);
36 static gboolean gst_subtitle_base_parse_sink_event(GstPad *pad, GstObject *parent, GstEvent *event);
37 static GstStateChangeReturn gst_subtitle_base_parse_change_state(GstElement *element, GstStateChange transition);
38 static GstFlowReturn gst_subtitle_base_parse_chain(GstPad *sinkpad, GstObject *parent, GstBuffer *buf);
39 static GstFlowReturn default_handle_buffer(GstSubtitleBaseParse *self);
40 static gboolean default_handle_sink_event(GstSubtitleBaseParse *self, GstEvent *event);
41 static void gst_subtitle_base_parse_pfn_init(GstSubtitleBaseParseClass *klass);
42
gst_subtitle_base_parse_get_type(void)43 GType gst_subtitle_base_parse_get_type(void)
44 {
45 static volatile gsize subtitle_base_type = 0;
46
47 if (g_once_init_enter(&subtitle_base_type)) {
48 static const GTypeInfo subtitle_base_info = {sizeof(GstSubtitleBaseParseClass),
49 nullptr, nullptr, (GClassInitFunc)gst_subtitle_base_parse_class_init, nullptr, nullptr,
50 sizeof(GstSubtitleBaseParse), 0, (GInstanceInitFunc)gst_subtitle_base_parse_init, nullptr};
51
52 GType type = g_type_register_static(GST_TYPE_ELEMENT, "GstSubtitleBaseParse", &subtitle_base_info,
53 G_TYPE_FLAG_ABSTRACT);
54 g_once_init_leave(&subtitle_base_type, type);
55 }
56
57 return (GType)subtitle_base_type;
58 }
59
60 /* free subtitle buffer */
free_buffer_context(GstSubtitleBaseParse * base_parse)61 static void free_buffer_context(GstSubtitleBaseParse *base_parse)
62 {
63 g_return_if_fail(base_parse != nullptr);
64 GstSubtitleBufferContext *buf_ctx = &base_parse->buffer_ctx;
65
66 if (buf_ctx->adapter != nullptr) {
67 gst_adapter_clear(buf_ctx->adapter);
68 g_object_unref(buf_ctx->adapter);
69 buf_ctx->adapter = nullptr;
70 }
71
72 if (buf_ctx->text != nullptr) {
73 (void)g_string_free(buf_ctx->text, (gboolean)TRUE);
74 buf_ctx->text = nullptr;
75 }
76 }
77
gst_subtitle_base_parse_dispose(GObject * object)78 static void gst_subtitle_base_parse_dispose(GObject *object)
79 {
80 g_return_if_fail(object != nullptr);
81 GstSubtitleBaseParse *base_parse = static_cast<GstSubtitleBaseParse *>((void *)object);
82
83 GST_DEBUG_OBJECT(base_parse, "cleaning up subtitle parser");
84
85 if (base_parse->segment != nullptr) {
86 gst_segment_free(base_parse->segment);
87 base_parse->segment = nullptr;
88 }
89
90 if (base_parse->event_segment != nullptr) {
91 gst_segment_free(base_parse->event_segment);
92 base_parse->event_segment = nullptr;
93 }
94
95 g_free(base_parse->language);
96 base_parse->language = nullptr;
97
98 free_buffer_context(base_parse);
99 g_mutex_clear(&base_parse->buffermutex);
100 g_mutex_clear(&base_parse->segmentmutex);
101 g_mutex_clear(&base_parse->pushmutex);
102
103 free_subinfos_and_streams(base_parse);
104
105 G_OBJECT_CLASS(g_parentClass)->dispose(object);
106 }
107
gst_subtitle_base_parse_class_init(GstSubtitleBaseParseClass * klass)108 static void gst_subtitle_base_parse_class_init(GstSubtitleBaseParseClass *klass)
109 {
110 GST_DEBUG_CATEGORY_INIT(gst_subtitle_base_parse_debug_category, "subtitlebaseparse", 0, "subtitle base parse");
111
112 GObjectClass *object_class = (GObjectClass *)klass;
113 GstElementClass *element_class = (GstElementClass *)klass;
114
115 g_parentClass = static_cast<GstElementClass *>(g_type_class_peek_parent(klass));
116 element_class->change_state = gst_subtitle_base_parse_change_state;
117 object_class->dispose = gst_subtitle_base_parse_dispose;
118
119 GstPadTemplate *src_pad_template = gst_pad_template_new("text_%u", GST_PAD_SRC, GST_PAD_SOMETIMES, GST_CAPS_ANY);
120 if (src_pad_template != nullptr) {
121 gst_element_class_add_pad_template((GstElementClass *)klass, src_pad_template);
122 GST_INFO("added a SOMETIMES src pad template");
123 }
124 gst_subtitle_base_parse_pfn_init(klass);
125 }
126
gst_subtitle_base_parse_pfn_init(GstSubtitleBaseParseClass * klass)127 static void gst_subtitle_base_parse_pfn_init(GstSubtitleBaseParseClass *klass)
128 {
129 klass->decode_frame_pfn = nullptr;
130 klass->get_srcpad_caps_pfn = nullptr;
131 klass->read_frame_pfn = nullptr;
132 klass->on_seek_pfn = nullptr;
133 klass->handle_buffer_pfn = default_handle_buffer;
134 klass->on_sink_event_pfn = default_handle_sink_event;
135 }
136
137 /* initialize the subtitle data stream buffer */
init_buffer_context(GstSubtitleBaseParse * base_parse)138 static void init_buffer_context(GstSubtitleBaseParse *base_parse)
139 {
140 g_return_if_fail(base_parse != nullptr);
141
142 /* initialize the external subtitle buffer */
143 GstSubtitleBufferContext *buf_ctx = &base_parse->buffer_ctx;
144
145 if (memset_s(buf_ctx, sizeof(GstSubtitleBufferContext), 0, sizeof(GstSubtitleBufferContext)) != EOK) {
146 GST_ERROR_OBJECT(base_parse, "memset_s failed");
147 }
148
149 buf_ctx->adapter = gst_adapter_new();
150 g_return_if_fail(buf_ctx->adapter != nullptr);
151
152 buf_ctx->text = g_string_new(nullptr);
153 if (buf_ctx->text == nullptr) {
154 g_object_unref(buf_ctx->adapter);
155 buf_ctx->adapter = nullptr;
156 return;
157 }
158
159 if (buf_ctx->text->str == nullptr) {
160 (void)g_string_free(buf_ctx->text, (gboolean)TRUE);
161 buf_ctx->text = nullptr;
162 g_object_unref(buf_ctx->adapter);
163 buf_ctx->adapter = nullptr;
164 return;
165 }
166 }
167
gst_subtitle_base_parse_init(GstSubtitleBaseParse * base_parse,gpointer gst_class)168 static void gst_subtitle_base_parse_init(GstSubtitleBaseParse *base_parse, gpointer gst_class)
169 {
170 g_return_if_fail((base_parse != nullptr) && (gst_class != nullptr));
171
172 gint index;
173
174 base_parse->srcpadtmpl = gst_element_class_get_pad_template(GST_ELEMENT_CLASS(gst_class), "text_%u");
175
176 GST_DEBUG_OBJECT(base_parse, "creating sink pad");
177
178 GstPadTemplate *sink_pad_template = gst_element_class_get_pad_template(GST_ELEMENT_CLASS(gst_class), "sink");
179 g_return_if_fail(sink_pad_template != nullptr);
180
181 GstPad *sinkpad = gst_pad_new_from_template(sink_pad_template, "sink");
182 g_return_if_fail(sinkpad != nullptr);
183
184 GST_DEBUG_OBJECT(base_parse, "setting functions on sink pad");
185 gst_pad_set_chain_function(sinkpad, gst_subtitle_base_parse_chain);
186 gst_pad_set_event_function(sinkpad, gst_subtitle_base_parse_sink_event);
187 base_parse->sinkpad = sinkpad;
188 GST_DEBUG_OBJECT(base_parse, "adding sink pad");
189 (void)gst_element_add_pad(GST_ELEMENT(base_parse), sinkpad);
190
191 base_parse->flushing = FALSE;
192 base_parse->segment = gst_segment_new();
193 gst_segment_init((GstSegment *)(base_parse->segment), GST_FORMAT_TIME);
194 base_parse->event_segment = gst_segment_new();
195 gst_segment_init((GstSegment *)(base_parse->event_segment), GST_FORMAT_TIME);
196
197 base_parse->need_segment = TRUE;
198 base_parse->from_internal = FALSE;
199 base_parse->recv_eos = FALSE;
200 base_parse->first_buffer = FALSE;
201 base_parse->stream_id = -1;
202 base_parse->language = nullptr;
203 init_buffer_context(base_parse);
204
205 base_parse->got_streams = FALSE;
206 base_parse->last_seekseq = 0;
207 base_parse->stream_num = 0;
208 base_parse->pad_num = 0;
209
210 for (index = 0; index < MAX_SUB_STREAM_NUM; index++) {
211 base_parse->subinfos[index] = nullptr;
212 base_parse->streams[index] = nullptr;
213 }
214
215 base_parse->seek_snap_after = FALSE;
216 base_parse->switching = FALSE;
217 base_parse->first_segment = TRUE;
218 g_mutex_init(&base_parse->buffermutex);
219 g_mutex_init((GMutex *)&base_parse->pushmutex);
220 g_mutex_init(&base_parse->segmentmutex);
221 }
222
default_handle_buffer(GstSubtitleBaseParse * self)223 static GstFlowReturn default_handle_buffer(GstSubtitleBaseParse *self)
224 {
225 GstFlowReturn ret = GST_FLOW_OK;
226 GstSubtitleFrame frame;
227 GstSubtitleDecodedFrame decoded_frame;
228
229 g_return_val_if_fail(self != nullptr, ret);
230 GstSubtitleBaseParseClass *baseclass = GST_SUBTITLE_BASE_PARSE_GET_CLASS(self);
231 g_return_val_if_fail(baseclass != nullptr, ret);
232
233 while (!self->flushing) {
234 /* determine whether the subclass overrides read_frame_pfn() and decode_frame_pfn() */
235 if (baseclass->read_frame_pfn == nullptr) {
236 GST_ERROR_OBJECT(self, "have not override ReadFrame function");
237 break;
238 }
239 if (baseclass->decode_frame_pfn == nullptr) {
240 GST_ERROR_OBJECT(self, "have not override DecodeFrame function");
241 break;
242 }
243
244 frame.data = nullptr;
245 frame.len = 0;
246
247 g_mutex_lock(&self->buffermutex);
248 /* read_frame_pfn: return value == 0 means that a full frame of subtitles cannot be read in current buffer */
249 gsize consumed = baseclass->read_frame_pfn(self, &frame);
250 g_mutex_unlock(&self->buffermutex);
251
252 if (G_UNLIKELY(consumed == 0)) {
253 g_free(frame.data);
254 frame.data = nullptr;
255 break;
256 }
257
258 if (memset_s(&decoded_frame, sizeof(GstSubtitleDecodedFrame), 0, sizeof(GstSubtitleDecodedFrame)) != EOK) {
259 GST_ERROR_OBJECT(self, "memset_s failed");
260 g_free(frame.data);
261 return ret;
262 }
263 decoded_frame.stream_index = self->stream_id;
264 /* decode a frame of subtitles */
265 gboolean err_ret = decode_one_frame(baseclass, self, &frame, &decoded_frame);
266 if (!err_ret) {
267 continue;
268 }
269
270 /* encapsulate the decoded frame of subtitles into a gstbuffer, and push it to srcpad */
271 ret = gst_subtitle_push_buffer(self, &decoded_frame);
272 gst_subtitle_free_frame(self, &decoded_frame);
273 }
274
275 return ret;
276 }
277
gst_subtitle_base_parse_chain(GstPad * sinkpad,GstObject * parent,GstBuffer * buf)278 static GstFlowReturn gst_subtitle_base_parse_chain(GstPad *sinkpad, GstObject *parent, GstBuffer *buf)
279 {
280 g_return_val_if_fail((parent != nullptr) && (buf != nullptr), GST_FLOW_NOT_LINKED);
281
282 GstFlowReturn ret = GST_FLOW_OK;
283 GstSubtitleBaseParse *self = static_cast<GstSubtitleBaseParse *>((void *)parent);
284 GstSubtitleBaseParseClass *baseclass = GST_SUBTITLE_BASE_PARSE_GET_CLASS(self);
285 g_return_val_if_fail(baseclass != nullptr, GST_FLOW_NOT_LINKED);
286
287 /* for first buffer, we must determine whether it is internal or external */
288 if (!handle_first_frame(sinkpad, buf, self)) {
289 gst_buffer_unref(buf);
290 return ret;
291 }
292
293 g_return_val_if_fail(get_subtitle_streams(baseclass, buf, self), ret);
294
295 if (G_UNLIKELY(!self->has_send_stream_start)) {
296 gst_subtitle_push_stream_start_event(self);
297 self->has_send_stream_start = TRUE;
298 }
299
300 g_return_val_if_fail(chain_set_caps_and_tags(self), GST_FLOW_EOS);
301
302 g_return_val_if_fail(!self->flushing, ret);
303
304 g_return_val_if_fail(chain_push_new_segment_event(ret, self), ret);
305
306 g_return_val_if_fail(baseclass->handle_buffer_pfn != nullptr, ret);
307 return baseclass->handle_buffer_pfn(self);
308 }
309
sink_event_handle_segment_eos(GstEvent * event,GstSubtitleBaseParse * self)310 static gboolean sink_event_handle_segment_eos(GstEvent *event, GstSubtitleBaseParse *self)
311 {
312 gboolean ret = FALSE;
313
314 self->recv_eos = TRUE;
315 GstBuffer *buf = gst_buffer_new_and_alloc(3); // alloc 3 bytes ('\r', '\n', '\0') for copy
316 if (buf != nullptr) {
317 const gchar term_chars[] = {'\r', '\n', '\0'};
318 GST_DEBUG_OBJECT(self, "EOS, Pushing remaining text");
319 (void)gst_buffer_fill(buf, 0, term_chars, 3); // copy 3 bytes from term_chars to buf at offset 0
320 gst_buffer_set_size(buf, 2); // set the total size 2 of the memory blocks in buf
321 GST_BUFFER_OFFSET(buf) = self->offset;
322 (void)gst_subtitle_base_parse_chain(self->sinkpad, (GstObject *)self, buf);
323 }
324
325 g_return_val_if_fail(self->sinkpad != nullptr, ret);
326 ret = gst_pad_event_default(self->sinkpad, (GstObject *)self, event);
327 return ret;
328 }
329
sink_event_handle_segment_event(GstEvent * event,GstSubtitleBaseParse * self)330 static gboolean sink_event_handle_segment_event(GstEvent *event, GstSubtitleBaseParse *self)
331 {
332 g_return_val_if_fail((event != nullptr) && (self != nullptr) && (self->segment != nullptr), FALSE);
333
334 const GstSegment *s = nullptr;
335 gboolean need_tags = FALSE;
336
337 gst_event_parse_segment(event, &s);
338 if ((s != nullptr) && (s->format == GST_FORMAT_TIME)) {
339 gst_event_copy_segment(event, self->segment);
340 gst_event_copy_segment(event, self->event_segment);
341 }
342 GST_DEBUG_OBJECT(self, "newsegment (%s)", gst_format_get_name(self->segment->format));
343
344 g_mutex_lock(&self->segmentmutex);
345 self->need_segment = TRUE;
346 self->switching = FALSE;
347 g_mutex_unlock(&self->segmentmutex);
348
349 gst_event_unref(event);
350 event = nullptr;
351 GST_INFO_OBJECT(self, "subtitle base receive segment event with 0x%06" PRIXPTR ", stream_id: %d, "
352 "start: %" G_GUINT64_FORMAT ", stop: %" G_GUINT64_FORMAT, FAKE_POINTER(&self->segment),
353 self->stream_id, self->segment->start, self->segment->stop);
354 if (self->need_srcpad_caps) {
355 if (gst_subtitle_set_caps(self)) {
356 self->need_srcpad_caps = FALSE;
357 need_tags = TRUE;
358 }
359 }
360 if (need_tags) {
361 g_return_val_if_fail(gst_subtitle_set_tags(self), TRUE);
362 }
363 return TRUE;
364 }
365
366 /* clear subtitle buffer */
clear_buffer(GstSubtitleBaseParse * base_parse)367 static void clear_buffer(GstSubtitleBaseParse *base_parse)
368 {
369 g_return_if_fail(base_parse != nullptr);
370
371 GstSubtitleBufferContext *buf_ctx = &base_parse->buffer_ctx;
372
373 if (buf_ctx->adapter != nullptr) {
374 gst_adapter_clear(buf_ctx->adapter);
375 }
376
377 if (buf_ctx->text != nullptr) {
378 (void)g_string_truncate(buf_ctx->text, 0);
379 }
380 }
381
sink_event_handle_flush_start(GstEvent * event,GstSubtitleBaseParse * self)382 static gboolean sink_event_handle_flush_start(GstEvent *event, GstSubtitleBaseParse *self)
383 {
384 g_return_val_if_fail((event != nullptr) && (self != nullptr) && (self->sinkpad != nullptr), FALSE);
385
386 gboolean ret = FALSE;
387 GstSubtitleBaseParseClass *baseclass = GST_SUBTITLE_BASE_PARSE_GET_CLASS(self);
388 g_return_val_if_fail(baseclass != nullptr, FALSE);
389
390 self->recv_eos = FALSE;
391 g_mutex_lock(&self->buffermutex);
392 self->flushing = TRUE;
393 clear_buffer(self);
394 g_mutex_unlock(&self->buffermutex);
395
396 if (self->switching) {
397 gst_event_unref(event);
398 event = nullptr;
399 GstStructure *change_state =
400 gst_structure_new("Changestate_flag", "Changestate_flag", G_TYPE_BOOLEAN, FALSE, nullptr);
401 g_return_val_if_fail(change_state != nullptr, ret);
402
403 GstEvent *flush_event = gst_event_new_custom(GST_EVENT_FLUSH_START, change_state);
404 if (flush_event != nullptr) {
405 ret = gst_pad_event_default(self->sinkpad, (GstObject *)self, flush_event);
406 }
407 } else {
408 ret = gst_pad_event_default(self->sinkpad, (GstObject *)self, event);
409 }
410 GST_INFO_OBJECT(self, "subtitle base flushing streams, stream_id: %d", self->stream_id);
411
412 return ret;
413 }
414
sink_event_handle_flush_stop(GstEvent * event,GstSubtitleBaseParse * self)415 static gboolean sink_event_handle_flush_stop(GstEvent *event, GstSubtitleBaseParse *self)
416 {
417 gboolean ret = FALSE;
418
419 g_return_val_if_fail((event != nullptr) && (self != nullptr) && (self->sinkpad != nullptr), FALSE);
420
421 self->flushing = FALSE;
422
423 if (self->switching) {
424 gst_event_unref(event);
425 event = gst_event_new_flush_stop(FALSE);
426 }
427
428 ret = gst_pad_event_default(self->sinkpad, (GstObject *)self, event);
429
430 GST_INFO_OBJECT(self, "subtitle base flushed streams, stream_id: %d", self->stream_id);
431
432 return ret;
433 }
434
sink_event_handle_caps_event(GstEvent * event,GstSubtitleBaseParse * self)435 static void sink_event_handle_caps_event(GstEvent *event, GstSubtitleBaseParse *self)
436 {
437 g_return_if_fail((event != nullptr) && (self != nullptr));
438
439 GstCaps *caps = nullptr;
440 gboolean internal = FALSE;
441
442 gst_event_parse_caps(event, &caps);
443 g_return_if_fail(caps != nullptr);
444
445 gchar *str_caps = gst_caps_to_string(caps);
446 GstStructure *structure = gst_caps_get_structure(caps, 0);
447 g_return_if_fail(structure != nullptr);
448 if (!gst_structure_get_boolean(structure, "parsed", &internal)) {
449 internal = FALSE;
450 }
451 const gchar *language = gst_structure_get_string(structure, "language");
452 if (language != nullptr) {
453 g_free(self->language);
454 self->language = gst_subtitle_str_dup(language, FALSE, 0);
455 }
456 self->from_internal = internal;
457
458 g_free(str_caps);
459 }
460
default_handle_sink_event(GstSubtitleBaseParse * self,GstEvent * event)461 static gboolean default_handle_sink_event(GstSubtitleBaseParse *self, GstEvent *event)
462 {
463 gboolean ret = FALSE;
464
465 g_return_val_if_fail((self != nullptr) && (event != nullptr), FALSE);
466
467 GST_INFO_OBJECT(self, "Handling %s event", GST_EVENT_TYPE_NAME(event));
468 switch (GST_EVENT_TYPE(event)) {
469 case GST_EVENT_EOS: {
470 ret = sink_event_handle_segment_eos(event, self);
471 break;
472 }
473 case GST_EVENT_SEGMENT: {
474 ret = sink_event_handle_segment_event(event, self);
475 break;
476 }
477 case GST_EVENT_FLUSH_START: {
478 ret = sink_event_handle_flush_start(event, self);
479 break;
480 }
481 case GST_EVENT_FLUSH_STOP: {
482 ret = sink_event_handle_flush_stop(event, self);
483 break;
484 }
485 case GST_EVENT_CAPS: {
486 sink_event_handle_caps_event(event, self);
487 g_return_val_if_fail(self->sinkpad != nullptr, ret);
488 ret = gst_pad_event_default(self->sinkpad, (GstObject *)self, event);
489 break;
490 }
491 case GST_EVENT_STREAM_START: {
492 g_return_val_if_fail(self->sinkpad != nullptr, ret);
493 ret = gst_pad_event_default(self->sinkpad, (GstObject *)self, event);
494 break;
495 }
496 default: {
497 g_return_val_if_fail(self->sinkpad != nullptr, ret);
498 ret = gst_pad_event_default(self->sinkpad, (GstObject *)self, event);
499 break;
500 }
501 }
502
503 return ret;
504 }
505
gst_subtitle_base_parse_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)506 static gboolean gst_subtitle_base_parse_sink_event(GstPad *pad, GstObject *parent, GstEvent *event)
507 {
508 (void)pad;
509
510 GstSubtitleBaseParse *self = static_cast<GstSubtitleBaseParse *>((void *)parent);
511 GstSubtitleBaseParseClass *baseclass = GST_SUBTITLE_BASE_PARSE_GET_CLASS(self);
512 gboolean ret = baseclass->on_sink_event_pfn(self, event);
513
514 return ret;
515 }
516
gst_subtitle_base_parse_change_state(GstElement * element,GstStateChange transition)517 static GstStateChangeReturn gst_subtitle_base_parse_change_state(GstElement *element, GstStateChange transition)
518 {
519 GstSubtitleBaseParse *self = (GstSubtitleBaseParse *)element;
520 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
521
522 g_return_val_if_fail(self != nullptr, GST_STATE_CHANGE_FAILURE);
523
524 switch (transition) {
525 case GST_STATE_CHANGE_READY_TO_PAUSED: {
526 /* format detection will init the parser state */
527 self->offset = 0;
528 self->need_srcpad_caps = TRUE;
529 self->first_buffer = TRUE;
530 self->recv_eos = FALSE;
531 self->first_segment = TRUE;
532 break;
533 }
534 default: {
535 break;
536 }
537 }
538
539 if (g_parentClass != nullptr) {
540 GstElementClass *element_class = (GstElementClass *)g_parentClass;
541 if (element_class->change_state != nullptr) {
542 ret = element_class->change_state(element, transition);
543 }
544 }
545
546 g_return_val_if_fail(ret != GST_STATE_CHANGE_FAILURE, ret);
547
548 switch (transition) {
549 case GST_STATE_CHANGE_PAUSED_TO_READY: {
550 self->need_srcpad_caps = TRUE;
551 break;
552 }
553 default: {
554 break;
555 }
556 }
557
558 return ret;
559 }
560