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 }