• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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