1 /*
2 * Copyright (C) 2021 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 "config.h"
17 #include "gst_mem_sink.h"
18 #include <cinttypes>
19 #include "gst_surface_mem_sink.h"
20 #include "gst_shared_mem_sink.h"
21
22 #define POINTER_MASK 0x00FFFFFF
23 #define FAKE_POINTER(addr) (POINTER_MASK & reinterpret_cast<uintptr_t>(addr))
24
25 namespace {
26 constexpr guint32 DEFAULT_PROP_MAX_POOL_CAPACITY = 5; // 0 is meanlessly
27 constexpr guint32 DEFAULT_PROP_WAIT_TIME = 5000; // us, 0 is meanlessly
28 }
29
30 struct _GstMemSinkPrivate {
31 GstCaps *caps;
32 GMutex mutex;
33 gboolean started;
34 GstMemSinkCallbacks callbacks;
35 gpointer userdata;
36 GDestroyNotify notify;
37 };
38
39 enum {
40 PROP_0,
41 PROP_CAPS,
42 PROP_MAX_POOL_CAPACITY,
43 PROP_WAIT_TIME
44 };
45
46 static GstStaticPadTemplate g_sinktemplate = GST_STATIC_PAD_TEMPLATE("sink",
47 GST_PAD_SINK,
48 GST_PAD_ALWAYS,
49 GST_STATIC_CAPS_ANY);
50
51 static void gst_mem_sink_dispose(GObject *obj);
52 static void gst_mem_sink_finalize(GObject *obj);
53 static void gst_mem_sink_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
54 static void gst_mem_sink_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
55 static GstCaps *gst_mem_sink_get_caps(GstBaseSink *basesink, GstCaps *filter);
56 static gboolean gst_mem_sink_set_caps(GstBaseSink *basesink, GstCaps *caps);
57 static GstCaps *gst_mem_sink_getcaps(GstMemSink *mem_sink);
58 static void gst_mem_sink_setcaps(GstMemSink *mem_sink, const GstCaps *caps);
59 static gboolean gst_mem_sink_event(GstBaseSink *basesink, GstEvent *event);
60 static gboolean gst_mem_sink_query(GstBaseSink *basesink, GstQuery *query);
61 static gboolean gst_mem_sink_start(GstBaseSink *basesink);
62 static gboolean gst_mem_sink_stop(GstBaseSink *basesink);
63 static GstFlowReturn gst_mem_sink_preroll(GstBaseSink *basesink, GstBuffer *buffer);
64 static GstFlowReturn gst_mem_sink_stream_render(GstBaseSink *basesink, GstBuffer *buffer);
65 static gboolean gst_mem_sink_propose_allocation(GstBaseSink *basesink, GstQuery *query);
66
67 #define gst_mem_sink_parent_class parent_class
68 G_DEFINE_ABSTRACT_TYPE_WITH_CODE(GstMemSink, gst_mem_sink, GST_TYPE_BASE_SINK, G_ADD_PRIVATE(GstMemSink));
69
70 GST_DEBUG_CATEGORY_STATIC(gst_mem_sink_debug_category);
71 #define GST_CAT_DEFAULT gst_mem_sink_debug_category
72
gst_mem_sink_class_init(GstMemSinkClass * klass)73 static void gst_mem_sink_class_init(GstMemSinkClass *klass)
74 {
75 g_return_if_fail(klass != nullptr);
76
77 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
78 GstBaseSinkClass *base_sink_class = GST_BASE_SINK_CLASS(klass);
79 GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
80
81 gst_element_class_add_static_pad_template(element_class, &g_sinktemplate);
82
83 gst_element_class_set_static_metadata(element_class,
84 "MemSink", "Generic/Sink",
85 "Output to memory and allow the application to get access to the memory",
86 "OpenHarmony");
87
88 gobject_class->dispose = gst_mem_sink_dispose;
89 gobject_class->finalize = gst_mem_sink_finalize;
90 gobject_class->set_property = gst_mem_sink_set_property;
91 gobject_class->get_property = gst_mem_sink_get_property;
92
93 g_object_class_install_property(gobject_class, PROP_CAPS,
94 g_param_spec_boxed("caps", "Caps",
95 "The allowed caps for the sink pad", GST_TYPE_CAPS,
96 (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
97
98 g_object_class_install_property(gobject_class, PROP_MAX_POOL_CAPACITY,
99 g_param_spec_uint("max-pool-capacity", "Max Pool Capacity",
100 "The maximum capacity of the buffer pool (0 == meanlessly)",
101 0, G_MAXUINT, DEFAULT_PROP_MAX_POOL_CAPACITY,
102 (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
103
104 g_object_class_install_property(gobject_class, PROP_WAIT_TIME,
105 g_param_spec_uint("wait-time", "Wait Time",
106 "The longest waiting time for single try to acquire buffer from buffer pool (0 == meanlessly)",
107 0, G_MAXUINT, DEFAULT_PROP_MAX_POOL_CAPACITY,
108 (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
109
110 base_sink_class->start = gst_mem_sink_start;
111 base_sink_class->stop = gst_mem_sink_stop;
112 base_sink_class->preroll = gst_mem_sink_preroll;
113 base_sink_class->render = gst_mem_sink_stream_render;
114 base_sink_class->get_caps = gst_mem_sink_get_caps;
115 base_sink_class->set_caps = gst_mem_sink_set_caps;
116 base_sink_class->query = gst_mem_sink_query;
117 base_sink_class->event = gst_mem_sink_event;
118 base_sink_class->propose_allocation = gst_mem_sink_propose_allocation;
119
120 GST_DEBUG_CATEGORY_INIT(gst_mem_sink_debug_category, "memsink", 0, "memsink class");
121 }
122
gst_mem_sink_init(GstMemSink * mem_sink)123 static void gst_mem_sink_init(GstMemSink *mem_sink)
124 {
125 g_return_if_fail(mem_sink != nullptr);
126
127 auto priv = reinterpret_cast<GstMemSinkPrivate *>(gst_mem_sink_get_instance_private(mem_sink));
128 g_return_if_fail(priv != nullptr);
129 mem_sink->priv = priv;
130
131 g_mutex_init(&priv->mutex);
132
133 mem_sink->max_pool_capacity = DEFAULT_PROP_MAX_POOL_CAPACITY;
134 mem_sink->wait_time = DEFAULT_PROP_WAIT_TIME;
135 priv->started = FALSE;
136 priv->caps = nullptr;
137 priv->callbacks.eos = nullptr;
138 priv->callbacks.new_preroll = nullptr;
139 priv->callbacks.new_sample = nullptr;
140 priv->userdata = nullptr;
141 priv->notify = nullptr;
142 }
143
gst_mem_sink_dispose(GObject * obj)144 static void gst_mem_sink_dispose(GObject *obj)
145 {
146 g_return_if_fail(obj != nullptr);
147
148 GstMemSink *mem_sink = GST_MEM_SINK_CAST(obj);
149 GstMemSinkPrivate *priv = mem_sink->priv;
150 g_return_if_fail(priv != nullptr);
151
152 GST_OBJECT_LOCK(mem_sink);
153 if (priv->caps != nullptr) {
154 gst_caps_unref(priv->caps);
155 priv->caps = nullptr;
156 }
157 if (priv->notify != nullptr) {
158 priv->notify(priv->userdata);
159 priv->userdata = nullptr;
160 priv->notify = nullptr;
161 }
162 GST_OBJECT_UNLOCK(mem_sink);
163
164 G_OBJECT_CLASS(parent_class)->dispose(obj);
165 }
166
gst_mem_sink_finalize(GObject * obj)167 static void gst_mem_sink_finalize(GObject *obj)
168 {
169 g_return_if_fail(obj != nullptr);
170
171 GstMemSink *mem_sink = GST_MEM_SINK_CAST(obj);
172 GstMemSinkPrivate *priv = mem_sink->priv;
173 g_return_if_fail(priv != nullptr);
174
175 g_mutex_clear(&priv->mutex);
176
177 G_OBJECT_CLASS(parent_class)->finalize(obj);
178 }
179
gst_mem_sink_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)180 static void gst_mem_sink_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
181 {
182 g_return_if_fail(object != nullptr);
183 g_return_if_fail(value != nullptr);
184 g_return_if_fail(pspec != nullptr);
185
186 GstMemSink *mem_sink = GST_MEM_SINK_CAST(object);
187 GstMemSinkPrivate *priv = mem_sink->priv;
188 g_return_if_fail(priv != nullptr);
189
190 switch (prop_id) {
191 case PROP_CAPS:
192 gst_mem_sink_setcaps(mem_sink, gst_value_get_caps(value));
193 break;
194 case PROP_MAX_POOL_CAPACITY: {
195 GST_OBJECT_LOCK(mem_sink);
196 guint max_pool_capacity = g_value_get_uint(value);
197 if (max_pool_capacity != 0) {
198 mem_sink->max_pool_capacity = max_pool_capacity;
199 }
200 GST_DEBUG_OBJECT(mem_sink, "set max pool capacity: %u", mem_sink->max_pool_capacity);
201 GST_OBJECT_UNLOCK(mem_sink);
202 break;
203 }
204 case PROP_WAIT_TIME: {
205 GST_OBJECT_LOCK(mem_sink);
206 mem_sink->wait_time = g_value_get_uint(value);
207 GST_DEBUG_OBJECT(mem_sink, "set wait time: %d", mem_sink->wait_time);
208 GST_OBJECT_UNLOCK(mem_sink);
209 break;
210 }
211 default:
212 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
213 break;
214 }
215 }
216
gst_mem_sink_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)217 static void gst_mem_sink_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
218 {
219 g_return_if_fail(object != nullptr);
220
221 GstMemSink *mem_sink = GST_MEM_SINK_CAST(object);
222 GstMemSinkPrivate *priv = mem_sink->priv;
223 g_return_if_fail(priv != nullptr);
224
225 switch (prop_id) {
226 case PROP_CAPS: {
227 GstCaps *caps = gst_mem_sink_getcaps(mem_sink);
228 gst_value_set_caps(value, caps);
229 if (caps != nullptr) {
230 gst_caps_unref(caps);
231 }
232 break;
233 }
234 case PROP_MAX_POOL_CAPACITY: {
235 GST_OBJECT_LOCK(mem_sink);
236 g_value_set_uint(value, mem_sink->max_pool_capacity);
237 GST_OBJECT_UNLOCK(mem_sink);
238 break;
239 }
240 case PROP_WAIT_TIME: {
241 GST_OBJECT_LOCK(mem_sink);
242 g_value_set_uint(value, mem_sink->wait_time);
243 GST_OBJECT_UNLOCK(mem_sink);
244 break;
245 }
246 default:
247 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
248 break;
249 }
250 }
251
gst_mem_sink_set_callback(GstMemSink * mem_sink,GstMemSinkCallbacks * callbacks,gpointer user_data,GDestroyNotify notify)252 void gst_mem_sink_set_callback(GstMemSink *mem_sink, GstMemSinkCallbacks *callbacks,
253 gpointer user_data, GDestroyNotify notify)
254 {
255 g_return_if_fail(GST_IS_MEM_SINK(mem_sink));
256 g_return_if_fail(callbacks != nullptr);
257 GstMemSinkPrivate *priv = mem_sink->priv;
258 g_return_if_fail(priv != nullptr);
259
260 GST_OBJECT_LOCK(mem_sink);
261 GDestroyNotify old_notify = priv->notify;
262 if (old_notify) {
263 gpointer old_data = priv->userdata;
264 priv->userdata = nullptr;
265 priv->notify = nullptr;
266
267 GST_OBJECT_UNLOCK(mem_sink);
268 old_notify(old_data);
269 GST_OBJECT_LOCK(mem_sink);
270 }
271
272 priv->callbacks = *callbacks;
273 priv->userdata = user_data;
274 priv->notify = notify;
275 GST_OBJECT_UNLOCK(mem_sink);
276 }
277
gst_mem_sink_setcaps(GstMemSink * mem_sink,const GstCaps * caps)278 static void gst_mem_sink_setcaps(GstMemSink *mem_sink, const GstCaps *caps)
279 {
280 GstMemSinkPrivate *priv = mem_sink->priv;
281 g_return_if_fail(priv != nullptr);
282
283 GST_OBJECT_LOCK(mem_sink);
284
285 GstCaps *old = priv->caps;
286 if (old != caps) {
287 if (caps != nullptr) {
288 priv->caps = gst_caps_copy(caps);
289 } else {
290 priv->caps = nullptr;
291 }
292 if (old != nullptr) {
293 gst_caps_unref(old);
294 }
295 }
296
297 GST_OBJECT_UNLOCK(mem_sink);
298 }
299
gst_mem_sink_getcaps(GstMemSink * mem_sink)300 static GstCaps *gst_mem_sink_getcaps(GstMemSink *mem_sink)
301 {
302 g_return_val_if_fail(mem_sink != nullptr, nullptr);
303 GstMemSinkPrivate *priv = mem_sink->priv;
304 g_return_val_if_fail(priv != nullptr, nullptr);
305
306 GST_OBJECT_LOCK(mem_sink);
307 GstCaps *caps = priv->caps;
308 if (caps != nullptr) {
309 gst_caps_ref(caps);
310 }
311 GST_OBJECT_UNLOCK(mem_sink);
312
313 return caps;
314 }
315
gst_mem_sink_start(GstBaseSink * basesink)316 static gboolean gst_mem_sink_start(GstBaseSink *basesink)
317 {
318 GstMemSink *mem_sink = GST_MEM_SINK(basesink);
319 g_return_val_if_fail(mem_sink != nullptr, FALSE);
320 GstMemSinkPrivate *priv = mem_sink->priv;
321 g_return_val_if_fail(priv != nullptr, FALSE);
322
323 g_mutex_lock(&priv->mutex);
324 GST_INFO_OBJECT(mem_sink, "starting");
325 priv->started = TRUE;
326 g_mutex_unlock(&priv->mutex);
327
328 return TRUE;
329 }
330
gst_mem_sink_stop(GstBaseSink * basesink)331 static gboolean gst_mem_sink_stop(GstBaseSink *basesink)
332 {
333 GstMemSink *mem_sink = GST_MEM_SINK(basesink);
334 g_return_val_if_fail(mem_sink != nullptr, FALSE);
335 GstMemSinkPrivate *priv = mem_sink->priv;
336 g_return_val_if_fail(priv != nullptr, FALSE);
337
338 g_mutex_lock(&priv->mutex);
339 GST_INFO_OBJECT(mem_sink, "stopping");
340 priv->started = FALSE;
341 g_mutex_unlock(&priv->mutex);
342
343 return TRUE;
344 }
345
gst_mem_sink_event(GstBaseSink * basesink,GstEvent * event)346 static gboolean gst_mem_sink_event(GstBaseSink *basesink, GstEvent *event)
347 {
348 GstMemSink *mem_sink = GST_MEM_SINK_CAST(basesink);
349 g_return_val_if_fail(mem_sink != nullptr, FALSE);
350 GstMemSinkPrivate *priv = mem_sink->priv;
351 g_return_val_if_fail(priv != nullptr, FALSE);
352 g_return_val_if_fail(event != nullptr, FALSE);
353
354 switch (event->type) {
355 case GST_EVENT_EOS: {
356 GST_INFO_OBJECT(mem_sink, "receiving EOS");
357 if (priv->callbacks.eos != nullptr) {
358 priv->callbacks.eos(mem_sink, priv->userdata);
359 }
360 break;
361 }
362 default:
363 break;
364 }
365 return GST_BASE_SINK_CLASS(parent_class)->event(basesink, event);
366 }
367
gst_mem_sink_query(GstBaseSink * basesink,GstQuery * query)368 static gboolean gst_mem_sink_query(GstBaseSink *basesink, GstQuery *query)
369 {
370 GstMemSink *mem_sink = GST_MEM_SINK_CAST(basesink);
371 g_return_val_if_fail(mem_sink != nullptr, FALSE);
372 GstMemSinkPrivate *priv = mem_sink->priv;
373 g_return_val_if_fail(priv != nullptr, FALSE);
374 g_return_val_if_fail(query != nullptr, FALSE);
375
376 gboolean ret;
377 switch (GST_QUERY_TYPE(query)) {
378 case GST_QUERY_SEEKING: {
379 GstFormat fmt;
380 gst_query_parse_seeking(query, &fmt, nullptr, nullptr, nullptr);
381 gst_query_set_seeking(query, fmt, FALSE, 0, -1);
382 ret = TRUE;
383 break;
384 }
385 default:
386 ret = GST_BASE_SINK_CLASS(parent_class)->query(basesink, query);
387 break;
388 }
389
390 return ret;
391 }
392
gst_mem_sink_set_caps(GstBaseSink * basesink,GstCaps * caps)393 static gboolean gst_mem_sink_set_caps(GstBaseSink *basesink, GstCaps *caps)
394 {
395 GstMemSink *mem_sink = GST_MEM_SINK_CAST(basesink);
396 g_return_val_if_fail(mem_sink != nullptr, FALSE);
397 GstMemSinkPrivate *priv = mem_sink->priv;
398 g_return_val_if_fail(priv != nullptr, FALSE);
399
400 GST_OBJECT_LOCK(mem_sink);
401
402 gboolean ret = FALSE;
403 if (caps != nullptr && priv->caps != nullptr) {
404 caps = gst_caps_intersect_full(priv->caps, caps, GST_CAPS_INTERSECT_FIRST);
405 if (caps != nullptr) {
406 GST_INFO_OBJECT(mem_sink, "received caps");
407 gst_caps_unref(caps);
408 ret = TRUE;
409 }
410 }
411
412 GST_OBJECT_UNLOCK(mem_sink);
413 return ret;
414 }
415
gst_mem_sink_get_caps(GstBaseSink * basesink,GstCaps * filter)416 static GstCaps *gst_mem_sink_get_caps(GstBaseSink *basesink, GstCaps *filter)
417 {
418 GstMemSink *mem_sink = GST_MEM_SINK_CAST(basesink);
419 g_return_val_if_fail(mem_sink != nullptr, nullptr);
420 GstMemSinkPrivate *priv = mem_sink->priv;
421 g_return_val_if_fail(priv != nullptr, nullptr);
422
423 GST_OBJECT_LOCK(mem_sink);
424 GstCaps *caps = priv->caps;
425 if (caps != nullptr) {
426 if (filter != nullptr) {
427 caps = gst_caps_intersect_full(filter, caps, GST_CAPS_INTERSECT_FIRST);
428 } else {
429 gst_caps_ref(caps);
430 }
431 }
432 GST_OBJECT_UNLOCK(mem_sink);
433
434 return caps;
435 }
436
gst_mem_sink_preroll(GstBaseSink * basesink,GstBuffer * buffer)437 static GstFlowReturn gst_mem_sink_preroll(GstBaseSink *basesink, GstBuffer *buffer)
438 {
439 GstMemSink *mem_sink = GST_MEM_SINK_CAST(basesink);
440 g_return_val_if_fail(mem_sink != nullptr, GST_FLOW_ERROR);
441 GstMemSinkPrivate *priv = mem_sink->priv;
442 g_return_val_if_fail(priv != nullptr, GST_FLOW_ERROR);
443
444 g_mutex_lock(&priv->mutex);
445 if (!priv->started) {
446 GST_INFO_OBJECT(mem_sink, "we are not started");
447 g_mutex_unlock(&priv->mutex);
448 return GST_FLOW_FLUSHING;
449 }
450 g_mutex_unlock(&priv->mutex);
451
452 GST_INFO_OBJECT(mem_sink, "preroll buffer 0x%06" PRIXPTR "", FAKE_POINTER(buffer));
453
454 GstFlowReturn ret = GST_FLOW_OK;
455 if (priv->callbacks.new_preroll != nullptr) {
456 ret = priv->callbacks.new_preroll(mem_sink, buffer, priv->userdata);
457 }
458
459 return ret;
460 }
461
gst_mem_sink_stream_render(GstBaseSink * basesink,GstBuffer * buffer)462 static GstFlowReturn gst_mem_sink_stream_render(GstBaseSink *basesink, GstBuffer *buffer)
463 {
464 GstMemSink *mem_sink = GST_MEM_SINK_CAST(basesink);
465 g_return_val_if_fail(mem_sink != nullptr && buffer != nullptr, GST_FLOW_ERROR);
466 GstMemSinkPrivate *priv = mem_sink->priv;
467 g_return_val_if_fail(priv != nullptr, GST_FLOW_ERROR);
468 GstMemSinkClass *mem_sink_class = GST_MEM_SINK_GET_CLASS(mem_sink);
469 g_return_val_if_fail(mem_sink_class != nullptr, GST_FLOW_ERROR);
470
471 g_mutex_lock(&priv->mutex);
472 if (!priv->started) {
473 GST_INFO_OBJECT(mem_sink, "we are not started");
474 g_mutex_unlock(&priv->mutex);
475 return GST_FLOW_FLUSHING;
476 }
477 g_mutex_unlock(&priv->mutex);
478
479 GST_INFO_OBJECT(mem_sink, "stream render buffer 0x%06" PRIXPTR "", FAKE_POINTER(buffer));
480
481 GstFlowReturn ret = GST_FLOW_OK;
482 if (mem_sink_class->do_stream_render != nullptr) {
483 ret = mem_sink_class->do_stream_render(mem_sink, &buffer);
484 g_return_val_if_fail(ret == GST_FLOW_OK, ret);
485 }
486
487 if (priv->callbacks.new_sample != nullptr) {
488 ret = priv->callbacks.new_sample(mem_sink, buffer, priv->userdata);
489 }
490
491 // the basesink will unref the buffer.
492 return ret;
493 }
494
gst_mem_sink_propose_allocation(GstBaseSink * basesink,GstQuery * query)495 static gboolean gst_mem_sink_propose_allocation(GstBaseSink *basesink, GstQuery *query)
496 {
497 GstMemSink *mem_sink = GST_MEM_SINK_CAST(basesink);
498 g_return_val_if_fail(mem_sink != nullptr && query != nullptr, FALSE);
499 GstMemSinkClass *mem_sink_class = GST_MEM_SINK_GET_CLASS(mem_sink);
500 g_return_val_if_fail(mem_sink_class != nullptr, FALSE);
501
502 if (mem_sink_class->do_propose_allocation != nullptr) {
503 return mem_sink_class->do_propose_allocation(mem_sink, query);
504 }
505
506 return FALSE;
507 }
508
gst_mem_sink_app_render(GstMemSink * mem_sink,GstBuffer * buffer)509 GstFlowReturn gst_mem_sink_app_render(GstMemSink *mem_sink, GstBuffer *buffer)
510 {
511 g_return_val_if_fail(mem_sink != nullptr, GST_FLOW_ERROR);
512 GstMemSinkPrivate *priv = mem_sink->priv;
513 g_return_val_if_fail(priv != nullptr, GST_FLOW_ERROR);
514 GstMemSinkClass *mem_sink_class = GST_MEM_SINK_GET_CLASS(mem_sink);
515 g_return_val_if_fail(mem_sink_class != nullptr, GST_FLOW_ERROR);
516
517 g_mutex_lock(&priv->mutex);
518 if (!priv->started) {
519 GST_INFO_OBJECT(mem_sink, "we are not started");
520 g_mutex_unlock(&priv->mutex);
521 return GST_FLOW_FLUSHING;
522 }
523 g_mutex_unlock(&priv->mutex);
524
525 GST_INFO_OBJECT(mem_sink, "app render buffer 0x%06" PRIXPTR "", FAKE_POINTER(buffer));
526
527 GstFlowReturn ret = GST_FLOW_OK;
528 if (mem_sink_class->do_app_render != nullptr) {
529 ret = mem_sink_class->do_app_render(mem_sink, buffer, FALSE);
530 }
531
532 return ret;
533 }
534
gst_mem_sink_app_preroll_render(GstMemSink * mem_sink,GstBuffer * buffer)535 GstFlowReturn gst_mem_sink_app_preroll_render(GstMemSink *mem_sink, GstBuffer *buffer)
536 {
537 g_return_val_if_fail(mem_sink != nullptr, GST_FLOW_ERROR);
538 GstMemSinkPrivate *priv = mem_sink->priv;
539 g_return_val_if_fail(priv != nullptr, GST_FLOW_ERROR);
540 GstMemSinkClass *mem_sink_class = GST_MEM_SINK_GET_CLASS(mem_sink);
541 g_return_val_if_fail(mem_sink_class != nullptr, GST_FLOW_ERROR);
542
543 g_mutex_lock(&priv->mutex);
544 if (!priv->started) {
545 GST_INFO_OBJECT(mem_sink, "we are not started");
546 g_mutex_unlock(&priv->mutex);
547 return GST_FLOW_FLUSHING;
548 }
549 g_mutex_unlock(&priv->mutex);
550
551 GST_INFO_OBJECT(mem_sink, "app preroll render buffer 0x%06" PRIXPTR "", FAKE_POINTER(buffer));
552
553 GstFlowReturn ret = GST_FLOW_OK;
554 if (mem_sink_class->do_app_render != nullptr) {
555 ret = mem_sink_class->do_app_render(mem_sink, buffer, TRUE);
556 }
557
558 return ret;
559 }
560