• 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_sink.h"
17 #include <cinttypes>
18 #include <gst/gst.h>
19 #include "scope_guard.h"
20 
21 using namespace OHOS;
22 using namespace OHOS::Media;
23 #define POINTER_MASK 0x00FFFFFF
24 #define FAKE_POINTER(addr) (POINTER_MASK & reinterpret_cast<uintptr_t>(addr))
25 
26 enum {
27     PROP_0,
28     PROP_AUDIO_SINK,
29     PROP_SEGMENT_UPDATED,
30     PROP_CHANGE_TRACK,
31     RPOP_ENABLE_DISPLAY,
32 };
33 
34 struct _GstSubtitleSinkPrivate {
35     GstElement *audio_sink;
36     GMutex mutex;
37     guint64 time_rendered;
38     guint64 text_frame_duration;
39     GstSubtitleSinkCallbacks callbacks;
40     gpointer userdata;
41     std::unique_ptr<TaskQueue> timer_queue;
42 };
43 
44 static GstStaticPadTemplate g_sinktemplate = GST_STATIC_PAD_TEMPLATE("subtitlesink",
45     GST_PAD_SINK,
46     GST_PAD_ALWAYS,
47     GST_STATIC_CAPS_ANY);
48 
49 static void gst_subtitle_sink_finalize(GObject *obj);
50 static void gst_subtitle_sink_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
51 static void gst_subtitle_sink_handle_buffer(GstSubtitleSink *subtitle_sink,
52     GstBuffer *buffer, gboolean cancel, guint64 delayUs = 0ULL);
53 static void gst_subtitle_sink_cancel_not_executed_task(GstSubtitleSink *subtitle_sink);
54 static void gst_subtitle_sink_get_gst_buffer_info(GstBuffer *buffer, guint64 &pts, guint64 &duration);
55 static GstStateChangeReturn gst_subtitle_sink_change_state(GstElement *element, GstStateChange transition);
56 static gboolean gst_subtitle_sink_send_event(GstElement *element, GstEvent *event);
57 static GstFlowReturn gst_subtitle_sink_render(GstAppSink *appsink);
58 static GstFlowReturn gst_subtitle_sink_new_sample(GstAppSink *appsink, gpointer user_data);
59 static GstFlowReturn gst_subtitle_sink_new_preroll(GstAppSink *appsink, gpointer user_data);
60 static gboolean gst_subtitle_sink_start(GstBaseSink *basesink);
61 static gboolean gst_subtitle_sink_stop(GstBaseSink *basesink);
62 static gboolean gst_subtitle_sink_event(GstBaseSink *basesink, GstEvent *event);
63 static GstClockTime gst_subtitle_sink_update_reach_time(GstBaseSink *basesink, GstClockTime reach_time,
64     gboolean *need_drop_this_buffer);
65 static gboolean gst_subtitle_sink_need_drop_buffer(GstBaseSink *basesink,
66     GstSegment *segment, guint64 pts, guint64 pts_end);
67 
68 #define gst_subtitle_sink_parent_class parent_class
69 G_DEFINE_TYPE_WITH_CODE(GstSubtitleSink, gst_subtitle_sink,
70                         GST_TYPE_APP_SINK, G_ADD_PRIVATE(GstSubtitleSink));
71 
72 GST_DEBUG_CATEGORY_STATIC(gst_subtitle_sink_debug_category);
73 #define GST_CAT_DEFAULT gst_subtitle_sink_debug_category
74 
gst_subtitle_sink_class_init(GstSubtitleSinkClass * kclass)75 static void gst_subtitle_sink_class_init(GstSubtitleSinkClass *kclass)
76 {
77     g_return_if_fail(kclass != nullptr);
78 
79     GObjectClass *gobject_class = G_OBJECT_CLASS(kclass);
80     GstElementClass *element_class = GST_ELEMENT_CLASS(kclass);
81     GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS(kclass);
82     gst_element_class_add_static_pad_template(element_class, &g_sinktemplate);
83 
84     gst_element_class_set_static_metadata(element_class,
85         "SubtitleSink", "Sink/Subtitle", " Subtitle sink", "OpenHarmony");
86 
87     gobject_class->finalize = gst_subtitle_sink_finalize;
88     gobject_class->set_property = gst_subtitle_sink_set_property;
89     element_class->change_state = gst_subtitle_sink_change_state;
90     element_class->send_event = gst_subtitle_sink_send_event;
91     basesink_class->event = gst_subtitle_sink_event;
92     basesink_class->stop = gst_subtitle_sink_stop;
93     basesink_class->start = gst_subtitle_sink_start;
94     basesink_class->update_reach_time = gst_subtitle_sink_update_reach_time;
95     basesink_class->need_drop_buffer = gst_subtitle_sink_need_drop_buffer;
96 
97     g_object_class_install_property(gobject_class, PROP_AUDIO_SINK,
98         g_param_spec_pointer("audio-sink", "audio sink", "audio sink",
99             (GParamFlags)(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)));
100     g_object_class_install_property(gobject_class, PROP_SEGMENT_UPDATED,
101         g_param_spec_boolean("segment-updated", "audio segment updated", "audio segment updated",
102             FALSE, (GParamFlags)(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)));
103     g_object_class_install_property(gobject_class, PROP_CHANGE_TRACK,
104         g_param_spec_boolean("change-track", "change track", "select track change",
105             FALSE, (GParamFlags)(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)));
106     g_object_class_install_property(gobject_class, RPOP_ENABLE_DISPLAY,
107         g_param_spec_boolean("enable-display", "enable subtitle display", "enable subtitle display",
108             TRUE, (GParamFlags)(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)));
109 
110     GST_DEBUG_CATEGORY_INIT(gst_subtitle_sink_debug_category, "subtitlesink", 0, "subtitlesink class");
111 }
112 
gst_subtitle_sink_init(GstSubtitleSink * subtitle_sink)113 static void gst_subtitle_sink_init(GstSubtitleSink *subtitle_sink)
114 {
115     g_return_if_fail(subtitle_sink != nullptr);
116 
117     subtitle_sink->stop_render = FALSE;
118     subtitle_sink->have_first_segment = FALSE;
119     subtitle_sink->audio_segment_updated = FALSE;
120     subtitle_sink->preroll_buffer = nullptr;
121     subtitle_sink->rate = 1.0f;
122     subtitle_sink->segment_updated = FALSE;
123     subtitle_sink->is_changing_track = FALSE;
124     subtitle_sink->track_changing_position = 0;
125     subtitle_sink->enable_display = TRUE;
126     subtitle_sink->need_send_empty_buffer = FALSE;
127     gst_segment_init (&subtitle_sink->segment, GST_FORMAT_UNDEFINED);
128 
129     auto priv = reinterpret_cast<GstSubtitleSinkPrivate *>(gst_subtitle_sink_get_instance_private(subtitle_sink));
130     g_return_if_fail(priv != nullptr);
131     subtitle_sink->priv = priv;
132     g_mutex_init(&priv->mutex);
133 
134     priv->callbacks.new_sample = nullptr;
135     priv->userdata = nullptr;
136     priv->audio_sink = nullptr;
137     priv->timer_queue = std::make_unique<TaskQueue>("GstSubtitleSinkTask");
138 }
139 
gst_subtitle_sink_set_callback(GstSubtitleSink * subtitle_sink,GstSubtitleSinkCallbacks * callbacks,gpointer user_data,GDestroyNotify notify)140 void gst_subtitle_sink_set_callback(GstSubtitleSink *subtitle_sink, GstSubtitleSinkCallbacks *callbacks,
141     gpointer user_data, GDestroyNotify notify)
142 {
143     g_return_if_fail(GST_IS_SUBTITLE_SINK(subtitle_sink));
144     g_return_if_fail(callbacks != nullptr);
145     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
146     g_return_if_fail(priv != nullptr);
147 
148     GST_OBJECT_LOCK(subtitle_sink);
149     priv->callbacks = *callbacks;
150     priv->userdata = user_data;
151 
152     GstAppSinkCallbacks appsink_callback;
153     appsink_callback.new_sample = gst_subtitle_sink_new_sample;
154     appsink_callback.new_preroll = gst_subtitle_sink_new_preroll;
155     gst_app_sink_set_callbacks(reinterpret_cast<GstAppSink *>(subtitle_sink),
156         &appsink_callback, user_data, notify);
157     GST_OBJECT_UNLOCK(subtitle_sink);
158 }
159 
gst_subtitle_sink_set_audio_sink(GstSubtitleSink * subtitle_sink,gpointer audio_sink)160 static void gst_subtitle_sink_set_audio_sink(GstSubtitleSink *subtitle_sink, gpointer audio_sink)
161 {
162     g_return_if_fail(audio_sink != nullptr);
163 
164     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
165     g_mutex_lock(&priv->mutex);
166     priv->audio_sink = GST_ELEMENT_CAST(gst_object_ref(audio_sink));
167     GST_INFO_OBJECT(subtitle_sink, "get audio sink: %s", GST_ELEMENT_NAME(priv->audio_sink));
168     g_mutex_unlock(&priv->mutex);
169 }
170 
gst_subtitle_sink_need_drop_buffer(GstBaseSink * basesink,GstSegment * segment,guint64 pts,guint64 pts_end)171 static gboolean gst_subtitle_sink_need_drop_buffer(GstBaseSink *basesink,
172     GstSegment *segment, guint64 pts, guint64 pts_end)
173 {
174     auto subtitle_sink = GST_SUBTITLE_SINK(basesink);
175     g_return_val_if_fail(subtitle_sink != nullptr, FALSE);
176     if (!subtitle_sink->enable_display) {
177         GST_LOG_OBJECT(subtitle_sink, "subtitle display disabled, drop this buffer");
178         return TRUE;
179     }
180 
181     auto temp_segment = *segment;
182     if (subtitle_sink->is_changing_track) {
183         temp_segment.start = subtitle_sink->track_changing_position;
184     }
185     if (subtitle_sink->have_first_filter) {
186         temp_segment.start = subtitle_sink->init_position;
187     }
188     guint64 start = temp_segment.start;
189     if (pts <= start && start < pts_end) {
190         GST_DEBUG_OBJECT(subtitle_sink, "no need drop, segment start is intersects with buffer time range, pts"
191         " = %" GST_TIME_FORMAT ", pts end = %" GST_TIME_FORMAT " segment start = %"
192         GST_TIME_FORMAT, GST_TIME_ARGS(pts), GST_TIME_ARGS(pts_end), GST_TIME_ARGS(start));
193         return FALSE;
194     }
195     return G_LIKELY(!gst_segment_clip (&temp_segment, GST_FORMAT_TIME, pts, pts_end, NULL, NULL));
196 }
197 
gst_subtitle_sink_handle_buffer(GstSubtitleSink * subtitle_sink,GstBuffer * buffer,gboolean cancel,guint64 delayUs)198 static void gst_subtitle_sink_handle_buffer(GstSubtitleSink *subtitle_sink,
199     GstBuffer *buffer, gboolean cancel, guint64 delayUs)
200 {
201     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
202     g_return_if_fail(priv != nullptr);
203 
204     if (!subtitle_sink->enable_display) {
205         if (subtitle_sink->need_send_empty_buffer) {
206             subtitle_sink->need_send_empty_buffer = FALSE;
207         } else {
208             return;
209         }
210     }
211 
212     auto handler = std::make_shared<TaskHandler<void>>([=]() {
213         (void)priv->callbacks.new_sample(buffer, priv->userdata);
214         gst_buffer_unref(buffer);
215     });
216     priv->timer_queue->EnqueueTask(handler, cancel, delayUs);
217 }
218 
gst_subtitle_sink_cancel_not_executed_task(GstSubtitleSink * subtitle_sink)219 static void gst_subtitle_sink_cancel_not_executed_task(GstSubtitleSink *subtitle_sink)
220 {
221     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
222     g_return_if_fail(priv != nullptr);
223     auto handler = std::make_shared<TaskHandler<void>>([]() {});
224     priv->timer_queue->EnqueueTask(handler, true);
225 }
226 
gst_subtitle_sink_segment_updated(GstSubtitleSink * subtitle_sink)227 static void gst_subtitle_sink_segment_updated(GstSubtitleSink *subtitle_sink)
228 {
229     subtitle_sink->audio_segment_updated = TRUE;
230     if (G_LIKELY(subtitle_sink->have_first_segment && !subtitle_sink->segment_updated)) {
231         auto audio_base = GST_BASE_SINK(subtitle_sink->priv->audio_sink);
232         GST_OBJECT_LOCK(audio_base);
233         subtitle_sink->segment_updated = TRUE;
234         gst_segment_copy_into(&audio_base->segment, &subtitle_sink->segment);
235         GST_OBJECT_UNLOCK(audio_base);
236         g_mutex_lock(&subtitle_sink->segment_mutex);
237         g_cond_signal(&subtitle_sink->segment_cond);
238         g_mutex_unlock(&subtitle_sink->segment_mutex);
239     }
240 }
241 
gst_subtitle_sink_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)242 static void gst_subtitle_sink_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
243 {
244     g_return_if_fail(object != nullptr);
245     g_return_if_fail(value != nullptr);
246     g_return_if_fail(pspec != nullptr);
247     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST(object);
248     switch (prop_id) {
249         case PROP_AUDIO_SINK: {
250             gst_subtitle_sink_set_audio_sink(subtitle_sink, g_value_get_pointer(value));
251             break;
252         }
253         case PROP_SEGMENT_UPDATED: {
254             gst_subtitle_sink_segment_updated(subtitle_sink);
255             break;
256         }
257         case PROP_CHANGE_TRACK: {
258             GST_OBJECT_LOCK(subtitle_sink);
259             subtitle_sink->is_changing_track = g_value_get_boolean(value);
260             GST_OBJECT_UNLOCK(subtitle_sink);
261             break;
262         }
263         case RPOP_ENABLE_DISPLAY: {
264             GST_BASE_SINK_PREROLL_LOCK(subtitle_sink);
265             subtitle_sink->enable_display = g_value_get_boolean(value);
266             if (!subtitle_sink->enable_display) {
267                 GST_DEBUG_OBJECT(subtitle_sink, "disable display, send an empty buffer");
268                 subtitle_sink->need_send_empty_buffer = true;
269                 gst_subtitle_sink_handle_buffer(subtitle_sink, nullptr, TRUE);
270             }
271             GST_BASE_SINK_PREROLL_UNLOCK(subtitle_sink);
272             break;
273         }
274         default:
275             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
276             break;
277     }
278 }
279 
gst_subtitle_sink_change_state(GstElement * element,GstStateChange transition)280 static GstStateChangeReturn gst_subtitle_sink_change_state(GstElement *element, GstStateChange transition)
281 {
282     g_return_val_if_fail(element != nullptr, GST_STATE_CHANGE_FAILURE);
283     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK(element);
284     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
285     g_return_val_if_fail(priv != nullptr, GST_STATE_CHANGE_FAILURE);
286     switch (transition) {
287         case GST_STATE_CHANGE_READY_TO_PAUSED: {
288             subtitle_sink->stop_render = TRUE;
289             break;
290         }
291         case GST_STATE_CHANGE_PAUSED_TO_PLAYING: {
292             g_mutex_lock(&priv->mutex);
293             gint64 left_duration = priv->text_frame_duration - priv->time_rendered;
294             left_duration = left_duration > 0 ? left_duration : 0;
295             priv->time_rendered = gst_util_get_timestamp();
296             g_mutex_unlock(&priv->mutex);
297             if (subtitle_sink->preroll_buffer != nullptr) {
298                 GST_DEBUG_OBJECT(subtitle_sink, "text left duration is %" GST_TIME_FORMAT,
299                     GST_TIME_ARGS(left_duration));
300                 gst_subtitle_sink_handle_buffer(subtitle_sink, nullptr, FALSE, GST_TIME_AS_USECONDS(left_duration));
301             }
302             subtitle_sink->stop_render = FALSE;
303             break;
304         }
305         case GST_STATE_CHANGE_PLAYING_TO_PAUSED: {
306             g_mutex_lock(&priv->mutex);
307             subtitle_sink->stop_render = TRUE;
308             priv->time_rendered = gst_util_get_timestamp() - priv->time_rendered;
309             g_mutex_unlock(&priv->mutex);
310             gst_subtitle_sink_cancel_not_executed_task(subtitle_sink);
311             break;
312         }
313         case GST_STATE_CHANGE_PAUSED_TO_READY: {
314             subtitle_sink->stop_render = FALSE;
315             gst_subtitle_sink_handle_buffer(subtitle_sink, nullptr, TRUE);
316             GST_INFO_OBJECT(subtitle_sink, "subtitle sink stop");
317             break;
318         }
319         default:
320             break;
321     }
322     return GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
323 }
324 
gst_subtitle_sink_send_event(GstElement * element,GstEvent * event)325 static gboolean gst_subtitle_sink_send_event(GstElement *element, GstEvent *event)
326 {
327     g_return_val_if_fail(element != nullptr && event != nullptr, FALSE);
328     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK(element);
329     GstFormat seek_format;
330     GstSeekType start_type;
331     GstSeekType stop_type;
332     gint64 start;
333     gint64 stop;
334 
335     GST_DEBUG_OBJECT(subtitle_sink, "handling event name %s", GST_EVENT_TYPE_NAME(event));
336     switch (GST_EVENT_TYPE(event)) {
337         case GST_EVENT_SEEK: {
338             gst_event_parse_seek(event, &subtitle_sink->rate, &seek_format,
339                 &subtitle_sink->seek_flags, &start_type, &start, &stop_type, &stop);
340             GST_DEBUG_OBJECT(subtitle_sink, "parse seek rate: %f", subtitle_sink->rate);
341             break;
342         }
343         default:
344             break;
345     }
346     return GST_ELEMENT_CLASS(parent_class)->send_event(element, event);
347 }
348 
gst_subtitle_sink_get_gst_buffer_info(GstBuffer * buffer,guint64 & pts,guint64 & duration)349 static void gst_subtitle_sink_get_gst_buffer_info(GstBuffer *buffer, guint64 &pts, guint64 &duration)
350 {
351     pts = GST_BUFFER_PTS_IS_VALID(buffer) ? GST_BUFFER_PTS(buffer) : GST_CLOCK_TIME_NONE;
352     duration = GST_BUFFER_DURATION_IS_VALID(buffer) ? GST_BUFFER_DURATION(buffer) : GST_CLOCK_TIME_NONE;
353 }
354 
gst_subtitle_sink_new_preroll(GstAppSink * appsink,gpointer user_data)355 static GstFlowReturn gst_subtitle_sink_new_preroll(GstAppSink *appsink, gpointer user_data)
356 {
357     (void)user_data;
358     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST(appsink);
359     if (subtitle_sink->stop_render) {
360         GST_WARNING_OBJECT(subtitle_sink, "prepared buffer or playing to paused buffer, do not render");
361         return GST_FLOW_OK;
362     }
363     GstSample *sample = gst_app_sink_pull_preroll(appsink);
364     GstBuffer *buffer = gst_buffer_ref(gst_sample_get_buffer(sample));
365     ON_SCOPE_EXIT(0) {
366         gst_sample_unref(sample);
367     };
368     g_return_val_if_fail(buffer != nullptr, GST_FLOW_ERROR);
369 
370     if (subtitle_sink->preroll_buffer == buffer) {
371         gst_buffer_unref(buffer);
372         GST_DEBUG_OBJECT(subtitle_sink, "preroll buffer has been rendererd, no need render again");
373         return GST_FLOW_OK;
374     }
375 
376     GST_INFO_OBJECT(subtitle_sink, "app render preroll buffer 0x%06" PRIXPTR "", FAKE_POINTER(buffer));
377     guint64 pts = 0;
378     guint64 duration = 0;
379     gst_subtitle_sink_get_gst_buffer_info(buffer, pts, duration);
380     if (!GST_CLOCK_TIME_IS_VALID(pts) || !GST_CLOCK_TIME_IS_VALID(duration)) {
381         gst_buffer_unref(buffer);
382         GST_ERROR_OBJECT(subtitle_sink, "pts or duration invalid");
383         return GST_FLOW_OK;
384     }
385     guint64 pts_end = pts + duration;
386     auto time = GST_BASE_SINK(subtitle_sink)->segment.time;
387     if (pts > time) {
388         GST_DEBUG_OBJECT(subtitle_sink, "pts = %" GST_TIME_FORMAT ", pts end = %"
389             GST_TIME_FORMAT " segment time = %" GST_TIME_FORMAT ", not yet render time",
390             GST_TIME_ARGS(pts), GST_TIME_ARGS(pts_end), GST_TIME_ARGS(time));
391         gst_buffer_unref(buffer);
392         return GST_FLOW_OK;
393     }
394     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
395     g_mutex_lock(&priv->mutex);
396     duration = std::min(duration, pts_end - subtitle_sink->segment.start);
397     priv->text_frame_duration = duration / subtitle_sink->rate;
398     priv->time_rendered = 0ULL;
399     g_mutex_unlock(&priv->mutex);
400     GST_DEBUG_OBJECT(subtitle_sink, "preroll buffer pts is %" GST_TIME_FORMAT ", duration is %" GST_TIME_FORMAT,
401         GST_TIME_ARGS(pts), GST_TIME_ARGS(priv->text_frame_duration));
402     subtitle_sink->preroll_buffer = buffer;
403     gst_subtitle_sink_handle_buffer(subtitle_sink, buffer, TRUE, 0ULL);
404     return GST_FLOW_OK;
405 }
406 
gst_subtitle_sink_update_reach_time(GstBaseSink * basesink,GstClockTime reach_time,gboolean * need_drop_this_buffer)407 static GstClockTime gst_subtitle_sink_update_reach_time(GstBaseSink *basesink, GstClockTime reach_time,
408     gboolean *need_drop_this_buffer)
409 {
410     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK(basesink);
411     if (!subtitle_sink->enable_display) {
412         *need_drop_this_buffer = TRUE;
413         GST_DEBUG_OBJECT(subtitle_sink, "subtitle display disabled, drop this buffer");
414     }
415     return reach_time;
416 }
417 
gst_subtitle_sink_render(GstAppSink * appsink)418 static GstFlowReturn gst_subtitle_sink_render(GstAppSink *appsink)
419 {
420     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST(appsink);
421     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
422 
423     GstSample *sample = gst_app_sink_pull_sample(appsink);
424     GstBuffer *buffer = gst_buffer_ref(gst_sample_get_buffer(sample));
425     ON_SCOPE_EXIT(0) {
426         gst_sample_unref(sample);
427     };
428     g_return_val_if_fail(buffer != nullptr, GST_FLOW_ERROR);
429 
430     if (subtitle_sink->preroll_buffer == buffer) {
431         subtitle_sink->preroll_buffer = nullptr;
432         GST_DEBUG_OBJECT(subtitle_sink, "preroll buffer, no need render again");
433         gst_buffer_unref(buffer);
434         return GST_FLOW_OK;
435     }
436 
437     GST_INFO_OBJECT(subtitle_sink, "app render buffer 0x%06" PRIXPTR "", FAKE_POINTER(buffer));
438 
439     guint64 pts = 0;
440     guint64 duration = 0;
441     gst_subtitle_sink_get_gst_buffer_info(buffer, pts, duration);
442     if (!GST_CLOCK_TIME_IS_VALID(pts) || !GST_CLOCK_TIME_IS_VALID(duration)) {
443         GST_ERROR_OBJECT(subtitle_sink, "pts or duration invalid");
444         gst_buffer_unref(buffer);
445         return GST_FLOW_ERROR;
446     }
447 
448     g_mutex_lock(&priv->mutex);
449     guint64 start = subtitle_sink->segment.start;
450     if (subtitle_sink->have_first_filter) {
451         start = subtitle_sink->init_position;
452     }
453     if (subtitle_sink->is_changing_track) {
454         start = subtitle_sink->track_changing_position;
455     }
456     duration = std::min(duration, pts + duration - start);
457     priv->text_frame_duration = duration / subtitle_sink->rate;
458     priv->time_rendered = gst_util_get_timestamp();
459     GST_DEBUG_OBJECT(subtitle_sink, "buffer pts is %" GST_TIME_FORMAT ", duration is %" GST_TIME_FORMAT,
460         GST_TIME_ARGS(pts), GST_TIME_ARGS(priv->text_frame_duration));
461     g_mutex_unlock(&priv->mutex);
462 
463     gst_subtitle_sink_handle_buffer(subtitle_sink, buffer, TRUE, 0ULL);
464     gst_subtitle_sink_handle_buffer(subtitle_sink, nullptr, FALSE, GST_TIME_AS_USECONDS(priv->text_frame_duration));
465     return GST_FLOW_OK;
466 }
467 
gst_subtitle_sink_new_sample(GstAppSink * appsink,gpointer user_data)468 static GstFlowReturn gst_subtitle_sink_new_sample(GstAppSink *appsink, gpointer user_data)
469 {
470     (void)user_data;
471     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK(appsink);
472     g_return_val_if_fail(subtitle_sink != nullptr, GST_FLOW_ERROR);
473     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
474     g_return_val_if_fail(priv != nullptr, GST_FLOW_ERROR);
475     return gst_subtitle_sink_render(appsink);
476 }
477 
gst_subtitle_sink_start(GstBaseSink * basesink)478 static gboolean gst_subtitle_sink_start(GstBaseSink *basesink)
479 {
480     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST (basesink);
481     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
482 
483     GST_BASE_SINK_CLASS(parent_class)->start(basesink);
484     g_mutex_lock (&priv->mutex);
485     GST_DEBUG_OBJECT (subtitle_sink, "started");
486     priv->timer_queue->Start();
487     g_mutex_unlock (&priv->mutex);
488 
489     return TRUE;
490 }
491 
gst_subtitle_sink_stop(GstBaseSink * basesink)492 static gboolean gst_subtitle_sink_stop(GstBaseSink *basesink)
493 {
494     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST (basesink);
495     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
496 
497     g_mutex_lock (&priv->mutex);
498     GST_DEBUG_OBJECT (subtitle_sink, "stopping");
499     subtitle_sink->have_first_segment = FALSE;
500     subtitle_sink->preroll_buffer = nullptr;
501     subtitle_sink->stop_render = FALSE;
502     subtitle_sink->segment_updated = FALSE;
503     subtitle_sink->is_changing_track = FALSE;
504     subtitle_sink->track_changing_position = 0;
505     subtitle_sink->need_send_empty_buffer = FALSE;
506     priv->timer_queue->Stop();
507     g_mutex_unlock (&priv->mutex);
508     GST_BASE_SINK_CLASS(parent_class)->stop(basesink);
509     return TRUE;
510 }
511 
gst_subtitle_sink_handle_speed(GstBaseSink * basesink)512 static void gst_subtitle_sink_handle_speed(GstBaseSink *basesink)
513 {
514     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST(basesink);
515     std::swap(subtitle_sink->segment.rate, subtitle_sink->segment.applied_rate);
516 }
517 
gst_subtitle_sink_handle_audio_segment(GstBaseSink * basesink,const GstSegment * new_segment)518 static void gst_subtitle_sink_handle_audio_segment(GstBaseSink *basesink, const GstSegment *new_segment)
519 {
520     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST(basesink);
521     auto audio_base = GST_BASE_SINK(subtitle_sink->priv->audio_sink);
522     if (!subtitle_sink->audio_segment_updated) {
523         g_mutex_lock(&subtitle_sink->segment_mutex);
524         gint64 end_time = g_get_monotonic_time() + G_TIME_SPAN_SECOND / 2;
525         g_cond_wait_until(&subtitle_sink->segment_cond, &subtitle_sink->segment_mutex, end_time);
526         g_mutex_unlock(&subtitle_sink->segment_mutex);
527     }
528     if (!subtitle_sink->segment_updated) {
529         GST_OBJECT_LOCK(audio_base);
530         gst_segment_copy_into(&audio_base->segment, &subtitle_sink->segment);
531         GST_OBJECT_UNLOCK(audio_base);
532     }
533     subtitle_sink->segment.stop = new_segment->stop;
534     subtitle_sink->segment.duration = new_segment->duration;
535 }
536 
gst_subtitle_sink_handle_segment_event(GstBaseSink * basesink,GstEvent * event)537 static GstEvent* gst_subtitle_sink_handle_segment_event(GstBaseSink *basesink, GstEvent *event)
538 {
539     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST(basesink);
540     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
541     subtitle_sink->segment_updated = FALSE;
542     auto audio_base = GST_BASE_SINK(priv->audio_sink);
543     guint32 seqnum = gst_event_get_seqnum (event);
544     GstSegment new_segment;
545     gst_event_copy_segment (event, &new_segment);
546     GST_DEBUG_OBJECT (basesink, "received upstream segment %u", seqnum);
547     if (!subtitle_sink->have_first_segment) {
548         subtitle_sink->have_first_segment = TRUE;
549         subtitle_sink->have_first_filter = TRUE;
550         GST_WARNING_OBJECT(subtitle_sink, "recv first segment event");
551         new_segment.rate = audio_base->segment.applied_rate;
552         subtitle_sink->init_position = new_segment.start;
553         gst_segment_copy_into(&new_segment, &subtitle_sink->segment);
554         subtitle_sink->segment.start = audio_base->segment.time;
555     } else if (!subtitle_sink->is_changing_track) {
556         subtitle_sink->have_first_filter = FALSE;
557         gst_subtitle_sink_handle_audio_segment(basesink, &new_segment);
558         gst_subtitle_sink_handle_speed(basesink);
559         GST_DEBUG_OBJECT (basesink, "segment updated");
560     }
561     if (subtitle_sink->is_changing_track) {
562         subtitle_sink->track_changing_position = new_segment.start;
563     }
564     subtitle_sink->audio_segment_updated = FALSE;
565     subtitle_sink->segment_updated = TRUE;
566     subtitle_sink->rate = subtitle_sink->segment.rate;
567     auto new_event = gst_event_new_segment(&subtitle_sink->segment);
568     if (new_event != nullptr) {
569         gst_event_unref(event);
570         event = new_event;
571     }
572     return event;
573 }
574 
gst_subtitle_sink_handle_flush_start_event(GstBaseSink * basesink,GstEvent * event)575 static gboolean gst_subtitle_sink_handle_flush_start_event(GstBaseSink *basesink, GstEvent *event)
576 {
577     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST(basesink);
578     g_return_val_if_fail(subtitle_sink != nullptr, FALSE);
579     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
580     GST_DEBUG_OBJECT(subtitle_sink, "subtitle flush start");
581     gst_subtitle_sink_handle_buffer(subtitle_sink, nullptr, TRUE);
582     subtitle_sink->stop_render = FALSE;
583     subtitle_sink->audio_segment_updated = FALSE;
584     priv->time_rendered = 0;
585     return GST_BASE_SINK_CLASS(parent_class)->event(basesink, event);
586 }
587 
gst_subtitle_sink_handle_flush_stop_event(GstBaseSink * basesink,GstEvent * event)588 static gboolean gst_subtitle_sink_handle_flush_stop_event(GstBaseSink *basesink, GstEvent *event)
589 {
590     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST(basesink);
591     g_return_val_if_fail(subtitle_sink != nullptr, FALSE);
592     GST_DEBUG_OBJECT(subtitle_sink, "subtitle flush stop");
593     return GST_BASE_SINK_CLASS(parent_class)->event(basesink, event);
594 }
595 
gst_subtitle_sink_event(GstBaseSink * basesink,GstEvent * event)596 static gboolean gst_subtitle_sink_event(GstBaseSink *basesink, GstEvent *event)
597 {
598     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST(basesink);
599     g_return_val_if_fail(subtitle_sink != nullptr, FALSE);
600     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
601     g_return_val_if_fail(priv != nullptr, FALSE);
602     g_return_val_if_fail(event != nullptr, FALSE);
603     switch (event->type) {
604         case GST_EVENT_SEGMENT: {
605             if (priv->audio_sink == nullptr) {
606                 break;
607             }
608             GST_OBJECT_LOCK (basesink);
609             event = gst_subtitle_sink_handle_segment_event(basesink, event);
610             GST_OBJECT_UNLOCK(basesink);
611             break;
612         }
613         case GST_EVENT_EOS: {
614             GST_DEBUG_OBJECT(subtitle_sink, "received EOS");
615             break;
616         }
617         case GST_EVENT_FLUSH_START: {
618             return gst_subtitle_sink_handle_flush_start_event(basesink, event);
619         }
620         case GST_EVENT_FLUSH_STOP: {
621             return gst_subtitle_sink_handle_flush_stop_event(basesink, event);
622         }
623         default:
624             break;
625     }
626     return GST_BASE_SINK_CLASS(parent_class)->event(basesink, event);
627 }
628 
gst_subtitle_sink_finalize(GObject * obj)629 static void gst_subtitle_sink_finalize(GObject *obj)
630 {
631     g_return_if_fail(obj != nullptr);
632     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST(obj);
633     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
634     subtitle_sink->preroll_buffer = nullptr;
635     if (priv->audio_sink != nullptr) {
636         gst_object_unref(priv->audio_sink);
637         priv->audio_sink = nullptr;
638     }
639     priv->timer_queue = nullptr;
640     g_mutex_clear(&priv->mutex);
641     G_OBJECT_CLASS(parent_class)->finalize(obj);
642 }