• 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.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