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 }