• 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, stop_type;
331     gint64 start, stop;
332 
333     GST_DEBUG_OBJECT(subtitle_sink, "handling event name %s", GST_EVENT_TYPE_NAME(event));
334     switch (GST_EVENT_TYPE(event)) {
335         case GST_EVENT_SEEK: {
336             gst_event_parse_seek(event, &subtitle_sink->rate, &seek_format,
337                 &subtitle_sink->seek_flags, &start_type, &start, &stop_type, &stop);
338             GST_DEBUG_OBJECT(subtitle_sink, "parse seek rate: %f", subtitle_sink->rate);
339             break;
340         }
341         default:
342             break;
343     }
344     return GST_ELEMENT_CLASS(parent_class)->send_event(element, event);
345 }
346 
gst_subtitle_sink_get_gst_buffer_info(GstBuffer * buffer,guint64 & pts,guint64 & duration)347 static void gst_subtitle_sink_get_gst_buffer_info(GstBuffer *buffer, guint64 &pts, guint64 &duration)
348 {
349     pts = GST_BUFFER_PTS_IS_VALID(buffer) ? GST_BUFFER_PTS(buffer) : GST_CLOCK_TIME_NONE;
350     duration = GST_BUFFER_DURATION_IS_VALID(buffer) ? GST_BUFFER_DURATION(buffer) : GST_CLOCK_TIME_NONE;
351 }
352 
gst_subtitle_sink_new_preroll(GstAppSink * appsink,gpointer user_data)353 static GstFlowReturn gst_subtitle_sink_new_preroll(GstAppSink *appsink, gpointer user_data)
354 {
355     (void)user_data;
356     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST(appsink);
357     if (subtitle_sink->stop_render) {
358         GST_WARNING_OBJECT(subtitle_sink, "prepared buffer or playing to paused buffer, do not render");
359         return GST_FLOW_OK;
360     }
361     GstSample *sample = gst_app_sink_pull_preroll(appsink);
362     GstBuffer *buffer = gst_buffer_ref(gst_sample_get_buffer(sample));
363     ON_SCOPE_EXIT(0) {
364         gst_sample_unref(sample);
365     };
366     g_return_val_if_fail(buffer != nullptr, GST_FLOW_ERROR);
367 
368     if (subtitle_sink->preroll_buffer == buffer) {
369         gst_buffer_unref(buffer);
370         GST_DEBUG_OBJECT(subtitle_sink, "preroll buffer has been rendererd, no need render again");
371         return GST_FLOW_OK;
372     }
373 
374     GST_INFO_OBJECT(subtitle_sink, "app render preroll buffer 0x%06" PRIXPTR "", FAKE_POINTER(buffer));
375     guint64 pts = 0;
376     guint64 duration = 0;
377     gst_subtitle_sink_get_gst_buffer_info(buffer, pts, duration);
378     if (!GST_CLOCK_TIME_IS_VALID(pts) || !GST_CLOCK_TIME_IS_VALID(duration)) {
379         gst_buffer_unref(buffer);
380         GST_ERROR_OBJECT(subtitle_sink, "pts or duration invalid");
381         return GST_FLOW_OK;
382     }
383     guint64 pts_end = pts + duration;
384     auto time = GST_BASE_SINK(subtitle_sink)->segment.time;
385     if (pts > time) {
386         GST_DEBUG_OBJECT(subtitle_sink, "pts = %" GST_TIME_FORMAT ", pts end = %"
387             GST_TIME_FORMAT " segment time = %" GST_TIME_FORMAT ", not yet render time",
388             GST_TIME_ARGS(pts), GST_TIME_ARGS(pts_end), GST_TIME_ARGS(time));
389         gst_buffer_unref(buffer);
390         return GST_FLOW_OK;
391     }
392     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
393     g_mutex_lock(&priv->mutex);
394     duration = std::min(duration, pts_end - subtitle_sink->segment.start);
395     priv->text_frame_duration = duration / subtitle_sink->rate;
396     priv->time_rendered = 0ULL;
397     g_mutex_unlock(&priv->mutex);
398     GST_DEBUG_OBJECT(subtitle_sink, "preroll buffer pts is %" GST_TIME_FORMAT ", duration is %" GST_TIME_FORMAT,
399         GST_TIME_ARGS(pts), GST_TIME_ARGS(priv->text_frame_duration));
400     subtitle_sink->preroll_buffer = buffer;
401     gst_subtitle_sink_handle_buffer(subtitle_sink, buffer, TRUE, 0ULL);
402     return GST_FLOW_OK;
403 }
404 
gst_subtitle_sink_update_reach_time(GstBaseSink * basesink,GstClockTime reach_time,gboolean * need_drop_this_buffer)405 static GstClockTime gst_subtitle_sink_update_reach_time(GstBaseSink *basesink, GstClockTime reach_time,
406     gboolean *need_drop_this_buffer)
407 {
408     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK(basesink);
409     if (!subtitle_sink->enable_display) {
410         *need_drop_this_buffer = TRUE;
411         GST_DEBUG_OBJECT(subtitle_sink, "subtitle display disabled, drop this buffer");
412     }
413     return reach_time;
414 }
415 
gst_subtitle_sink_render(GstAppSink * appsink)416 static GstFlowReturn gst_subtitle_sink_render(GstAppSink *appsink)
417 {
418     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST(appsink);
419     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
420 
421     GstSample *sample = gst_app_sink_pull_sample(appsink);
422     GstBuffer *buffer = gst_buffer_ref(gst_sample_get_buffer(sample));
423     ON_SCOPE_EXIT(0) {
424         gst_sample_unref(sample);
425     };
426     g_return_val_if_fail(buffer != nullptr, GST_FLOW_ERROR);
427 
428     if (subtitle_sink->preroll_buffer == buffer) {
429         subtitle_sink->preroll_buffer = nullptr;
430         GST_DEBUG_OBJECT(subtitle_sink, "preroll buffer, no need render again");
431         gst_buffer_unref(buffer);
432         return GST_FLOW_OK;
433     }
434 
435     GST_INFO_OBJECT(subtitle_sink, "app render buffer 0x%06" PRIXPTR "", FAKE_POINTER(buffer));
436 
437     guint64 pts = 0;
438     guint64 duration = 0;
439     gst_subtitle_sink_get_gst_buffer_info(buffer, pts, duration);
440     if (!GST_CLOCK_TIME_IS_VALID(pts) || !GST_CLOCK_TIME_IS_VALID(duration)) {
441         GST_ERROR_OBJECT(subtitle_sink, "pts or duration invalid");
442         gst_buffer_unref(buffer);
443         return GST_FLOW_ERROR;
444     }
445 
446     g_mutex_lock(&priv->mutex);
447     guint64 start = subtitle_sink->segment.start;
448     if (subtitle_sink->have_first_filter) {
449         start = subtitle_sink->init_position;
450     }
451     if (subtitle_sink->is_changing_track) {
452         start = subtitle_sink->track_changing_position;
453     }
454     duration = std::min(duration, pts + duration - start);
455     priv->text_frame_duration = duration / subtitle_sink->rate;
456     priv->time_rendered = gst_util_get_timestamp();
457     GST_DEBUG_OBJECT(subtitle_sink, "buffer pts is %" GST_TIME_FORMAT ", duration is %" GST_TIME_FORMAT,
458         GST_TIME_ARGS(pts), GST_TIME_ARGS(priv->text_frame_duration));
459     g_mutex_unlock(&priv->mutex);
460 
461     gst_subtitle_sink_handle_buffer(subtitle_sink, buffer, TRUE, 0ULL);
462     gst_subtitle_sink_handle_buffer(subtitle_sink, nullptr, FALSE, GST_TIME_AS_USECONDS(priv->text_frame_duration));
463     return GST_FLOW_OK;
464 }
465 
gst_subtitle_sink_new_sample(GstAppSink * appsink,gpointer user_data)466 static GstFlowReturn gst_subtitle_sink_new_sample(GstAppSink *appsink, gpointer user_data)
467 {
468     (void)user_data;
469     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK(appsink);
470     g_return_val_if_fail(subtitle_sink != nullptr, GST_FLOW_ERROR);
471     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
472     g_return_val_if_fail(priv != nullptr, GST_FLOW_ERROR);
473     return gst_subtitle_sink_render(appsink);
474 }
475 
gst_subtitle_sink_start(GstBaseSink * basesink)476 static gboolean gst_subtitle_sink_start(GstBaseSink *basesink)
477 {
478     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST (basesink);
479     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
480 
481     GST_BASE_SINK_CLASS(parent_class)->start(basesink);
482     g_mutex_lock (&priv->mutex);
483     GST_DEBUG_OBJECT (subtitle_sink, "started");
484     priv->timer_queue->Start();
485     g_mutex_unlock (&priv->mutex);
486 
487     return TRUE;
488 }
489 
gst_subtitle_sink_stop(GstBaseSink * basesink)490 static gboolean gst_subtitle_sink_stop(GstBaseSink *basesink)
491 {
492     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST (basesink);
493     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
494 
495     g_mutex_lock (&priv->mutex);
496     GST_DEBUG_OBJECT (subtitle_sink, "stopping");
497     subtitle_sink->have_first_segment = FALSE;
498     subtitle_sink->preroll_buffer = nullptr;
499     subtitle_sink->stop_render = FALSE;
500     subtitle_sink->segment_updated = FALSE;
501     subtitle_sink->is_changing_track = FALSE;
502     subtitle_sink->track_changing_position = 0;
503     subtitle_sink->need_send_empty_buffer = FALSE;
504     priv->timer_queue->Stop();
505     g_mutex_unlock (&priv->mutex);
506     GST_BASE_SINK_CLASS(parent_class)->stop(basesink);
507     return TRUE;
508 }
509 
gst_subtitle_sink_handle_speed(GstBaseSink * basesink)510 static void gst_subtitle_sink_handle_speed(GstBaseSink *basesink)
511 {
512     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST(basesink);
513     std::swap(subtitle_sink->segment.rate, subtitle_sink->segment.applied_rate);
514 }
515 
gst_subtitle_sink_handle_audio_segment(GstBaseSink * basesink,const GstSegment * new_segment)516 static void gst_subtitle_sink_handle_audio_segment(GstBaseSink *basesink, const GstSegment *new_segment)
517 {
518     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST(basesink);
519     auto audio_base = GST_BASE_SINK(subtitle_sink->priv->audio_sink);
520     if (!subtitle_sink->audio_segment_updated) {
521         g_mutex_lock(&subtitle_sink->segment_mutex);
522         gint64 end_time = g_get_monotonic_time() + G_TIME_SPAN_SECOND / 2;
523         g_cond_wait_until(&subtitle_sink->segment_cond, &subtitle_sink->segment_mutex, end_time);
524         g_mutex_unlock(&subtitle_sink->segment_mutex);
525     }
526     if (!subtitle_sink->segment_updated) {
527         GST_OBJECT_LOCK(audio_base);
528         gst_segment_copy_into(&audio_base->segment, &subtitle_sink->segment);
529         GST_OBJECT_UNLOCK(audio_base);
530     }
531     subtitle_sink->segment.stop = new_segment->stop;
532     subtitle_sink->segment.duration = new_segment->duration;
533 }
534 
gst_subtitle_sink_handle_segment_event(GstBaseSink * basesink,GstEvent * event)535 static GstEvent* gst_subtitle_sink_handle_segment_event(GstBaseSink *basesink, GstEvent *event)
536 {
537     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST(basesink);
538     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
539     subtitle_sink->segment_updated = FALSE;
540     auto audio_base = GST_BASE_SINK(priv->audio_sink);
541     guint32 seqnum = gst_event_get_seqnum (event);
542     GstSegment new_segment;
543     gst_event_copy_segment (event, &new_segment);
544     GST_DEBUG_OBJECT (basesink, "received upstream segment %u", seqnum);
545     if (!subtitle_sink->have_first_segment) {
546         subtitle_sink->have_first_segment = TRUE;
547         subtitle_sink->have_first_filter = TRUE;
548         GST_WARNING_OBJECT(subtitle_sink, "recv first segment event");
549         new_segment.rate = audio_base->segment.applied_rate;
550         subtitle_sink->init_position = new_segment.start;
551         gst_segment_copy_into(&new_segment, &subtitle_sink->segment);
552         subtitle_sink->segment.start = audio_base->segment.time;
553     } else if (!subtitle_sink->is_changing_track) {
554         subtitle_sink->have_first_filter = FALSE;
555         gst_subtitle_sink_handle_audio_segment(basesink, &new_segment);
556         gst_subtitle_sink_handle_speed(basesink);
557         GST_DEBUG_OBJECT (basesink, "segment updated");
558     }
559     if (subtitle_sink->is_changing_track) {
560         subtitle_sink->track_changing_position = new_segment.start;
561     }
562     subtitle_sink->audio_segment_updated = FALSE;
563     subtitle_sink->segment_updated = TRUE;
564     subtitle_sink->rate = subtitle_sink->segment.rate;
565     auto new_event = gst_event_new_segment(&subtitle_sink->segment);
566     if (new_event != nullptr) {
567         gst_event_unref(event);
568         event = new_event;
569     }
570     return event;
571 }
572 
gst_subtitle_sink_handle_flush_start_event(GstBaseSink * basesink,GstEvent * event)573 static gboolean gst_subtitle_sink_handle_flush_start_event(GstBaseSink *basesink, GstEvent *event)
574 {
575     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST(basesink);
576     g_return_val_if_fail(subtitle_sink != nullptr, FALSE);
577     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
578     GST_DEBUG_OBJECT(subtitle_sink, "subtitle flush start");
579     gst_subtitle_sink_handle_buffer(subtitle_sink, nullptr, TRUE);
580     subtitle_sink->stop_render = FALSE;
581     subtitle_sink->audio_segment_updated = FALSE;
582     priv->time_rendered = 0;
583     return GST_BASE_SINK_CLASS(parent_class)->event(basesink, event);
584 }
585 
gst_subtitle_sink_handle_flush_stop_event(GstBaseSink * basesink,GstEvent * event)586 static gboolean gst_subtitle_sink_handle_flush_stop_event(GstBaseSink *basesink, GstEvent *event)
587 {
588     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST(basesink);
589     g_return_val_if_fail(subtitle_sink != nullptr, FALSE);
590     GST_DEBUG_OBJECT(subtitle_sink, "subtitle flush stop");
591     return GST_BASE_SINK_CLASS(parent_class)->event(basesink, event);
592 }
593 
gst_subtitle_sink_event(GstBaseSink * basesink,GstEvent * event)594 static gboolean gst_subtitle_sink_event(GstBaseSink *basesink, GstEvent *event)
595 {
596     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST(basesink);
597     g_return_val_if_fail(subtitle_sink != nullptr, FALSE);
598     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
599     g_return_val_if_fail(priv != nullptr, FALSE);
600     g_return_val_if_fail(event != nullptr, FALSE);
601     switch (event->type) {
602         case GST_EVENT_SEGMENT: {
603             if (priv->audio_sink == nullptr) {
604                 break;
605             }
606             GST_OBJECT_LOCK (basesink);
607             event = gst_subtitle_sink_handle_segment_event(basesink, event);
608             GST_OBJECT_UNLOCK(basesink);
609             break;
610         }
611         case GST_EVENT_EOS: {
612             GST_DEBUG_OBJECT(subtitle_sink, "received EOS");
613             break;
614         }
615         case GST_EVENT_FLUSH_START: {
616             return gst_subtitle_sink_handle_flush_start_event(basesink, event);
617         }
618         case GST_EVENT_FLUSH_STOP: {
619             return gst_subtitle_sink_handle_flush_stop_event(basesink, event);
620         }
621         default:
622             break;
623     }
624     return GST_BASE_SINK_CLASS(parent_class)->event(basesink, event);
625 }
626 
gst_subtitle_sink_finalize(GObject * obj)627 static void gst_subtitle_sink_finalize(GObject *obj)
628 {
629     g_return_if_fail(obj != nullptr);
630     GstSubtitleSink *subtitle_sink = GST_SUBTITLE_SINK_CAST(obj);
631     GstSubtitleSinkPrivate *priv = subtitle_sink->priv;
632     subtitle_sink->preroll_buffer = nullptr;
633     if (priv->audio_sink != nullptr) {
634         gst_object_unref(priv->audio_sink);
635         priv->audio_sink = nullptr;
636     }
637     priv->timer_queue = nullptr;
638     g_mutex_clear(&priv->mutex);
639     G_OBJECT_CLASS(parent_class)->finalize(obj);
640 }