• 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 "gst_video_shmem_sink.h"
17 #include <cinttypes>
18 #include "gst/base/gstqueuearray.h"
19 #include "gst/video/video-info.h"
20 #include "gst_video_shmem_pool.h"
21 #include "gst_shmem_allocator_old.h"
22 #include "media_log.h"
23 
24 namespace {
25     constexpr guint32 DEFAULT_INTERNEL_QUEUE_INIT_SIZE = 10;
26     constexpr guint32 DEFAULT_PROP_MAX_POOL_CAPACITY = 0; // 0 means no limit
27     constexpr guint32 DEFAULT_PROP_MEM_PREFIX = 0;
28 }
29 
30 enum WaitStatus : guint32 {
31     NO_WAITING = 0,
32     USER_WAITING = 1 << 0,
33     STREAM_WAITING = 1 << 1,
34 };
35 
36 struct _GstVideoShMemSinkPrivate {
37     GstCaps *caps;
38     guint maxPoolCapacity;
39     GstQueueArray *queue;
40     GCond cond;
41     GMutex mutex;
42     GstBuffer *prerollBuffer;
43     GstCaps *prerollCaps;
44     GstCaps *lastCaps;
45     GstSegment prerollSegment;
46     GstSegment lastSegment;
47     gboolean started;
48     GstSample *sample;
49     guint32 waitStatus;
50     gboolean isEos;
51     gboolean flushing;
52     guint32 numBuffers;
53     gboolean unlock;
54     guint32 memPrefix;
55 };
56 
57 enum {
58     /* signals */
59     SIGNAL_NEW_PREROLL,
60     SIGNAL_NEW_SAMPLE,
61 
62     /* actions */
63     SIGNAL_PULL_PREROLL,
64     SIGNAL_PULL_SAMPLE,
65 
66     LAST_SIGNAL
67 };
68 
69 enum {
70     PROP_0,
71     PROP_CAPS,
72     PROP_MAX_POOL_CAPACITY,
73     PROP_MEM_PREFIX, // allocate the memory with the required length's prefix.
74 };
75 
76 static GstStaticPadTemplate g_sinktemplate = GST_STATIC_PAD_TEMPLATE("sink",
77     GST_PAD_SINK,
78     GST_PAD_ALWAYS,
79     GST_STATIC_CAPS_ANY);
80 
81 static void gst_video_shmem_sink_dispose(GObject *obj);
82 static void gst_video_shmem_sink_finalize(GObject *object);
83 static void gst_video_shmem_sink_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
84 static void gst_video_shmem_sink_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
85 static GstCaps *gst_video_shmem_sink_get_caps(GstBaseSink *basesink, GstCaps *filter);
86 static gboolean gst_video_shmem_sink_set_caps(GstBaseSink *basesink, GstCaps *caps);
87 static GstCaps *gst_video_shmem_sink_getcaps(GstVideoShMemSink *vidShMemSink);
88 static void gst_video_shmem_sink_setcaps(GstVideoShMemSink *vidShMemSink, const GstCaps *caps);
89 static gboolean gst_video_shmem_sink_event(GstBaseSink *basesink, GstEvent *event);
90 static gboolean gst_video_shmem_sink_query(GstBaseSink *basesink, GstQuery *query);
91 static gboolean gst_video_shmem_sink_start(GstBaseSink *basesink);
92 static gboolean gst_video_shmem_sink_stop(GstBaseSink *basesink);
93 static gboolean gst_video_shmem_sink_unlock_start(GstBaseSink *bsink);
94 static gboolean gst_video_shmem_sink_unlock_stop(GstBaseSink *bsink);
95 static GstFlowReturn gst_video_shmem_sink_preroll(GstBaseSink *basesink, GstBuffer *buffer);
96 static GstFlowReturn gst_video_shmem_sink_render(GstBaseSink *basesink, GstBuffer *buffer);
97 static gboolean gst_video_shmem_sink_propose_allocation(GstBaseSink *bsink, GstQuery *query);
98 static GstSample *gst_video_shmem_sink_pull_preroll(GstVideoShMemSink *sink);
99 static GstSample *gst_video_shmem_sink_pull_sample(GstVideoShMemSink *sink);
100 
101 static guint gst_video_shmem_sink_signals[LAST_SIGNAL] = { 0 };
102 
103 #define gst_video_shmem_sink_parent_class parent_class
104 G_DEFINE_TYPE_WITH_CODE(GstVideoShMemSink, gst_video_shmem_sink,
105     GST_TYPE_BASE_SINK, G_ADD_PRIVATE(GstVideoShMemSink));
106 
gst_video_shmem_sink_setup_gobject_class(GObjectClass * gobjectClass)107 static void gst_video_shmem_sink_setup_gobject_class(GObjectClass *gobjectClass)
108 {
109     gobjectClass->dispose = gst_video_shmem_sink_dispose;
110     gobjectClass->finalize = gst_video_shmem_sink_finalize;
111     gobjectClass->set_property = gst_video_shmem_sink_set_property;
112     gobjectClass->get_property = gst_video_shmem_sink_get_property;
113 
114     g_object_class_install_property(gobjectClass, PROP_CAPS,
115         g_param_spec_boxed ("caps", "Caps",
116             "The allowed caps for the sink pad", GST_TYPE_CAPS,
117             (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
118 
119     g_object_class_install_property(gobjectClass, PROP_MAX_POOL_CAPACITY,
120         g_param_spec_uint("max-pool-capacity", "Max Pool Capacity",
121             "The maximum capacity of the buffer pool (0 == unlimited)",
122             0, G_MAXUINT, DEFAULT_PROP_MAX_POOL_CAPACITY,
123             (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
124 
125     g_object_class_install_property(gobjectClass, PROP_MEM_PREFIX,
126         g_param_spec_uint("mem-prefix", "Memory Prefix",
127             "Allocate the memory with required length's prefix (in bytes)",
128             0, G_MAXUINT, DEFAULT_PROP_MEM_PREFIX,
129             (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
130 }
131 
gst_video_shmem_sink_class_init(GstVideoShMemSinkClass * klass)132 static void gst_video_shmem_sink_class_init(GstVideoShMemSinkClass *klass)
133 {
134     g_return_if_fail(klass != nullptr);
135 
136     GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
137     GstBaseSinkClass *baseSinkClass = GST_BASE_SINK_CLASS(klass);
138     GstElementClass *elementClass = GST_ELEMENT_CLASS(klass);
139 
140     gst_element_class_add_static_pad_template (elementClass, &g_sinktemplate);
141 
142     gst_element_class_set_static_metadata(elementClass,
143         "VideoShMemSink", "Sink/Video",
144         "Output to ashmem memory and allow the application to get access to the ashmem memory",
145         "OpenHarmony");
146 
147     gst_video_shmem_sink_setup_gobject_class(gobjectClass);
148 
149     gst_video_shmem_sink_signals[SIGNAL_NEW_PREROLL] =
150         g_signal_new("new-preroll", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST,
151             G_STRUCT_OFFSET(GstVideoShMemSinkClass, new_preroll),
152             nullptr, nullptr, nullptr, GST_TYPE_FLOW_RETURN, 0, G_TYPE_NONE);
153 
154     gst_video_shmem_sink_signals[SIGNAL_NEW_SAMPLE] =
155         g_signal_new("new-sample", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST,
156             G_STRUCT_OFFSET(GstVideoShMemSinkClass, new_preroll),
157             nullptr, nullptr, nullptr, GST_TYPE_FLOW_RETURN, 0, G_TYPE_NONE);
158 
159     gst_video_shmem_sink_signals[SIGNAL_PULL_PREROLL] =
160         g_signal_new("pull-preroll", G_TYPE_FROM_CLASS(klass), (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
161             G_STRUCT_OFFSET(GstVideoShMemSinkClass, pull_preroll),
162             nullptr, nullptr, nullptr, GST_TYPE_SAMPLE, 0, G_TYPE_NONE);
163 
164     gst_video_shmem_sink_signals[SIGNAL_PULL_SAMPLE] =
165         g_signal_new("pull-sample", G_TYPE_FROM_CLASS(klass), (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
166             G_STRUCT_OFFSET(GstVideoShMemSinkClass, pull_sample),
167             nullptr, nullptr, nullptr, GST_TYPE_SAMPLE, 0, G_TYPE_NONE);
168 
169     baseSinkClass->unlock = gst_video_shmem_sink_unlock_start;
170     baseSinkClass->unlock_stop = gst_video_shmem_sink_unlock_stop;
171     baseSinkClass->start = gst_video_shmem_sink_start;
172     baseSinkClass->stop = gst_video_shmem_sink_stop;
173     baseSinkClass->preroll = gst_video_shmem_sink_preroll;
174     baseSinkClass->render = gst_video_shmem_sink_render;
175     baseSinkClass->get_caps = gst_video_shmem_sink_get_caps;
176     baseSinkClass->set_caps = gst_video_shmem_sink_set_caps;
177     baseSinkClass->query = gst_video_shmem_sink_query;
178     baseSinkClass->event = gst_video_shmem_sink_event;
179     baseSinkClass->propose_allocation = gst_video_shmem_sink_propose_allocation;
180 
181     klass->pull_preroll = gst_video_shmem_sink_pull_preroll;
182     klass->pull_sample = gst_video_shmem_sink_pull_sample;
183 }
184 
gst_video_shmem_sink_init(GstVideoShMemSink * vidShmemSink)185 static void gst_video_shmem_sink_init(GstVideoShMemSink *vidShmemSink)
186 {
187     g_return_if_fail(vidShmemSink != nullptr);
188 
189     auto priv = reinterpret_cast<GstVideoShMemSinkPrivate *>(gst_video_shmem_sink_get_instance_private(vidShmemSink));
190     g_return_if_fail(priv != nullptr);
191     vidShmemSink->priv = priv;
192 
193     g_mutex_init(&priv->mutex);
194     g_cond_init(&priv->cond);
195     priv->queue = gst_queue_array_new(DEFAULT_INTERNEL_QUEUE_INIT_SIZE);
196     priv->sample = gst_sample_new(nullptr, nullptr, nullptr, nullptr);
197 
198     priv->maxPoolCapacity = DEFAULT_PROP_MAX_POOL_CAPACITY;
199     priv->started = FALSE;
200     priv->prerollBuffer = nullptr;
201     priv->prerollCaps = nullptr;
202     priv->caps = nullptr;
203     priv->waitStatus = WaitStatus::NO_WAITING;
204     priv->isEos = FALSE;
205     priv->flushing = FALSE;
206     priv->numBuffers = 0;
207     priv->unlock = FALSE;
208     priv->memPrefix = DEFAULT_PROP_MEM_PREFIX;
209 }
210 
gst_video_shmem_sink_dispose(GObject * obj)211 static void gst_video_shmem_sink_dispose(GObject *obj)
212 {
213     g_return_if_fail(obj != nullptr);
214 
215     GstVideoShMemSink *vidShmemSink = GST_VIDEO_SHMEM_SINK_CAST(obj);
216     GstVideoShMemSinkPrivate *priv = vidShmemSink->priv;
217     g_return_if_fail(priv != nullptr);
218 
219     GST_OBJECT_LOCK(vidShmemSink);
220     if (priv->caps != nullptr) {
221         gst_caps_unref(priv->caps);
222         priv->caps = nullptr;
223     }
224     GST_OBJECT_UNLOCK(vidShmemSink);
225 
226     g_mutex_lock(&priv->mutex);
227     GstMiniObject *queueObj = reinterpret_cast<GstMiniObject *>(gst_queue_array_pop_head(priv->queue));
228     while (queueObj != nullptr) {
229         gst_mini_object_unref(queueObj);
230         queueObj = reinterpret_cast<GstMiniObject *>(gst_queue_array_pop_head(priv->queue));
231     }
232     priv->numBuffers = 0;
233     (void)gst_buffer_replace(&priv->prerollBuffer, nullptr);
234     (void)gst_caps_replace(&priv->prerollCaps, nullptr);
235     (void)gst_caps_replace(&priv->lastCaps, nullptr);
236     if (priv->sample != nullptr) {
237         gst_sample_unref(priv->sample);
238         priv->sample = nullptr;
239     }
240     g_mutex_unlock(&priv->mutex);
241 
242     G_OBJECT_CLASS(parent_class)->dispose(obj);
243 }
244 
gst_video_shmem_sink_finalize(GObject * obj)245 static void gst_video_shmem_sink_finalize(GObject *obj)
246 {
247     g_return_if_fail(obj != nullptr);
248 
249     GstVideoShMemSink *vidShmemSink = GST_VIDEO_SHMEM_SINK_CAST(obj);
250     GstVideoShMemSinkPrivate *priv = vidShmemSink->priv;
251     g_return_if_fail(priv != nullptr);
252 
253     g_mutex_clear (&priv->mutex);
254     g_cond_clear (&priv->cond);
255     gst_queue_array_free(priv->queue);
256 
257     G_OBJECT_CLASS(parent_class)->finalize(obj);
258 }
259 
gst_video_shmem_sink_set_property(GObject * object,guint propId,const GValue * value,GParamSpec * pspec)260 static void gst_video_shmem_sink_set_property(GObject *object, guint propId, const GValue *value, GParamSpec *pspec)
261 {
262     g_return_if_fail(object != nullptr);
263 
264     GstVideoShMemSink *vidShmemSink = GST_VIDEO_SHMEM_SINK_CAST(object);
265     GstVideoShMemSinkPrivate *priv = vidShmemSink->priv;
266     g_return_if_fail(priv != nullptr);
267 
268     switch (propId) {
269         case PROP_CAPS:
270             gst_video_shmem_sink_setcaps(vidShmemSink, gst_value_get_caps(value));
271             break;
272         case PROP_MAX_POOL_CAPACITY:
273             priv->maxPoolCapacity = g_value_get_uint(value);
274             GST_INFO_OBJECT(vidShmemSink, "set max pool capacity: %u", priv->maxPoolCapacity);
275             break;
276         case PROP_MEM_PREFIX:
277             priv->memPrefix = g_value_get_uint(value);
278             GST_INFO_OBJECT(vidShmemSink, "set memprefix to %d", priv->memPrefix);
279             break;
280         default:
281             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
282             break;
283     }
284 }
285 
gst_video_shmem_sink_get_property(GObject * object,guint propId,GValue * value,GParamSpec * pspec)286 static void gst_video_shmem_sink_get_property(GObject *object, guint propId, GValue *value, GParamSpec *pspec)
287 {
288     g_return_if_fail(object != nullptr);
289 
290     GstVideoShMemSink *vidShmemSink = GST_VIDEO_SHMEM_SINK_CAST(object);
291     GstVideoShMemSinkPrivate *priv = vidShmemSink->priv;
292     g_return_if_fail(priv != nullptr);
293 
294     switch (propId) {
295         case PROP_CAPS: {
296             GstCaps *caps = gst_video_shmem_sink_getcaps(vidShmemSink);
297             gst_value_set_caps(value, caps);
298             if (caps != nullptr) {
299                 gst_caps_unref(caps);
300             }
301             break;
302         }
303         case PROP_MAX_POOL_CAPACITY:
304             g_value_set_uint(value, priv->maxPoolCapacity);
305             break;
306         case PROP_MEM_PREFIX:
307             g_value_set_uint(value, priv->memPrefix);
308             GST_INFO_OBJECT(vidShmemSink, "get memprefix to %d", priv->memPrefix);
309             break;
310         default:
311             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
312             break;
313     }
314 }
315 
gst_video_shmem_sink_setcaps(GstVideoShMemSink * vidShMemSink,const GstCaps * caps)316 static void gst_video_shmem_sink_setcaps(GstVideoShMemSink *vidShMemSink, const GstCaps *caps)
317 {
318     GstVideoShMemSinkPrivate *priv = vidShMemSink->priv;
319     g_return_if_fail(priv != nullptr);
320 
321     GST_OBJECT_LOCK(vidShMemSink);
322     GST_INFO_OBJECT(vidShMemSink, "setting caps to %s", gst_caps_to_string(caps));
323 
324     GstCaps *old = priv->caps;
325     if (old != caps) {
326         if (caps != nullptr) {
327             priv->caps = gst_caps_copy(caps);
328         } else {
329             priv->caps = nullptr;
330         }
331         if (old != nullptr) {
332             gst_caps_unref(old);
333         }
334     }
335 
336     GST_OBJECT_UNLOCK(vidShMemSink);
337 }
338 
gst_video_shmem_sink_getcaps(GstVideoShMemSink * vidShMemSink)339 static GstCaps *gst_video_shmem_sink_getcaps(GstVideoShMemSink *vidShMemSink)
340 {
341     GstVideoShMemSinkPrivate *priv = vidShMemSink->priv;
342     g_return_val_if_fail(priv != nullptr, nullptr);
343 
344     GST_OBJECT_LOCK(vidShMemSink);
345     GstCaps *caps = priv->caps;
346     if (caps != nullptr) {
347         (void)gst_caps_ref(caps);
348         GST_INFO_OBJECT(vidShMemSink, "getting caps of %s", gst_caps_to_string(caps));
349     }
350     GST_OBJECT_UNLOCK(vidShMemSink);
351 
352     return caps;
353 }
354 
gst_video_shmem_sink_unlock_start(GstBaseSink * bsink)355 static gboolean gst_video_shmem_sink_unlock_start(GstBaseSink *bsink)
356 {
357     GstVideoShMemSink *vidShMemSink = GST_VIDEO_SHMEM_SINK_CAST(bsink);
358     g_return_val_if_fail(vidShMemSink != nullptr, FALSE);
359     GstVideoShMemSinkPrivate *priv = vidShMemSink->priv;
360     g_return_val_if_fail(priv != nullptr, FALSE);
361 
362     g_mutex_lock(&priv->mutex);
363     GST_INFO_OBJECT(vidShMemSink, "unlock start");
364     priv->unlock = TRUE;
365     g_cond_signal(&priv->cond);
366     g_mutex_unlock(&priv->mutex);
367 
368     return TRUE;
369 }
370 
gst_video_shmem_sink_unlock_stop(GstBaseSink * bsink)371 static gboolean gst_video_shmem_sink_unlock_stop(GstBaseSink *bsink)
372 {
373     GstVideoShMemSink *vidShMemSink = GST_VIDEO_SHMEM_SINK_CAST(bsink);
374     g_return_val_if_fail(vidShMemSink != nullptr, FALSE);
375     GstVideoShMemSinkPrivate *priv = vidShMemSink->priv;
376     g_return_val_if_fail(priv != nullptr, FALSE);
377 
378     g_mutex_lock(&priv->mutex);
379     GST_INFO_OBJECT(vidShMemSink, "unlock stop");
380     priv->unlock = FALSE;
381     g_cond_signal(&priv->cond);
382     g_mutex_unlock(&priv->mutex);
383 
384     return TRUE;
385 }
386 
gst_video_shmem_sink_flush_unlocked(GstVideoShMemSink * vidShMemSink)387 static void gst_video_shmem_sink_flush_unlocked(GstVideoShMemSink *vidShMemSink)
388 {
389     GstVideoShMemSinkPrivate *priv = vidShMemSink->priv;
390     g_return_if_fail(priv != nullptr);
391 
392     GST_INFO_OBJECT(vidShMemSink, "flush stop video ashmem sink");
393     priv->isEos = FALSE;
394     (void)gst_buffer_replace(&priv->prerollBuffer, nullptr);
395     GstMiniObject *queueObj = reinterpret_cast<GstMiniObject *>(gst_queue_array_pop_head(priv->queue));
396     while (queueObj != nullptr) {
397         gst_mini_object_unref(queueObj);
398         queueObj = reinterpret_cast<GstMiniObject *>(gst_queue_array_pop_head(priv->queue));
399     }
400     priv->numBuffers = 0;
401     g_cond_signal(&priv->cond);
402 }
403 
gst_video_shmem_sink_start(GstBaseSink * bsink)404 static gboolean gst_video_shmem_sink_start(GstBaseSink *bsink)
405 {
406     GstVideoShMemSink *vidShMemSink = GST_VIDEO_SHMEM_SINK_CAST(bsink);
407     g_return_val_if_fail(vidShMemSink != nullptr, FALSE);
408     GstVideoShMemSinkPrivate *priv = vidShMemSink->priv;
409     g_return_val_if_fail(priv != nullptr, FALSE);
410 
411     g_mutex_lock(&priv->mutex);
412     GST_INFO_OBJECT(vidShMemSink, "starting");
413     priv->waitStatus = NO_WAITING;
414     priv->flushing = FALSE;
415     priv->started = TRUE;
416     gst_segment_init(&priv->prerollSegment, GST_FORMAT_TIME);
417     gst_segment_init(&priv->lastSegment, GST_FORMAT_TIME);
418     priv->sample = gst_sample_make_writable(priv->sample);
419     gst_sample_set_buffer(priv->sample, nullptr);
420     gst_sample_set_buffer_list(priv->sample, nullptr);
421     gst_sample_set_caps(priv->sample, nullptr);
422     gst_sample_set_segment(priv->sample, nullptr);
423     g_mutex_unlock(&priv->mutex);
424 
425     return TRUE;
426 }
427 
gst_video_shmem_sink_stop(GstBaseSink * bsink)428 static gboolean gst_video_shmem_sink_stop(GstBaseSink *bsink)
429 {
430     GstVideoShMemSink *vidShMemSink = GST_VIDEO_SHMEM_SINK_CAST(bsink);
431     g_return_val_if_fail(vidShMemSink != nullptr, FALSE);
432     GstVideoShMemSinkPrivate *priv = vidShMemSink->priv;
433     g_return_val_if_fail(priv != nullptr, FALSE);
434 
435     g_mutex_lock(&priv->mutex);
436     GST_INFO_OBJECT(vidShMemSink, "stopping");
437     priv->flushing = TRUE;
438     priv->started = FALSE;
439     priv->waitStatus = NO_WAITING;
440     gst_video_shmem_sink_flush_unlocked(vidShMemSink);
441     (void)gst_buffer_replace(&priv->prerollBuffer, nullptr);
442     (void)gst_caps_replace(&priv->prerollCaps, nullptr);
443     (void)gst_caps_replace(&priv->lastCaps, nullptr);
444     gst_segment_init(&priv->prerollSegment, GST_FORMAT_UNDEFINED);
445     gst_segment_init(&priv->lastSegment, GST_FORMAT_UNDEFINED);
446     g_mutex_unlock(&priv->mutex);
447 
448     return TRUE;
449 }
450 
WaitBufferDrained(GstVideoShMemSink * vidShMemSink)451 static gboolean WaitBufferDrained(GstVideoShMemSink *vidShMemSink)
452 {
453     GstVideoShMemSinkPrivate *priv = vidShMemSink->priv;
454 
455     g_mutex_lock(&priv->mutex);
456     while ((priv->numBuffers > 0) && (priv->flushing == FALSE)) {
457         if (priv->unlock) {
458             g_mutex_unlock(&priv->mutex);
459             if (gst_base_sink_wait_preroll(GST_BASE_SINK_CAST(vidShMemSink)) != GST_FLOW_OK) {
460                 return FALSE;
461             }
462             g_mutex_lock(&priv->mutex);
463             continue;
464         }
465         priv->waitStatus |= STREAM_WAITING;
466         g_cond_wait(&priv->cond, &priv->mutex);
467         priv->waitStatus &= ~STREAM_WAITING;
468     }
469     g_mutex_unlock(&priv->mutex);
470 
471     return TRUE;
472 }
473 
gst_video_shmem_sink_event(GstBaseSink * bsink,GstEvent * event)474 static gboolean gst_video_shmem_sink_event(GstBaseSink *bsink, GstEvent *event)
475 {
476     GstVideoShMemSink *vidShMemSink = GST_VIDEO_SHMEM_SINK_CAST(bsink);
477     g_return_val_if_fail(vidShMemSink != nullptr, FALSE);
478     GstVideoShMemSinkPrivate *priv = vidShMemSink->priv;
479     g_return_val_if_fail(priv != nullptr, FALSE);
480 
481     switch (event->type) {
482         case GST_EVENT_SEGMENT:
483             g_mutex_lock(&priv->mutex);
484             GST_INFO_OBJECT(vidShMemSink, "receiving SGEMENT");
485             gst_queue_array_push_tail(priv->queue, gst_event_ref(event));
486             if (priv->prerollBuffer == nullptr) {
487                 gst_event_copy_segment(event, &priv->prerollSegment);
488             }
489             g_mutex_unlock(&priv->mutex);
490             break;
491         case GST_EVENT_EOS: {
492             g_mutex_lock(&priv->mutex);
493             GST_INFO_OBJECT(vidShMemSink, "receiving EOS");
494             priv->isEos = TRUE;
495             g_cond_signal(&priv->cond);
496             g_mutex_unlock(&priv->mutex);
497 
498             if (!WaitBufferDrained(vidShMemSink)) {
499                 gst_event_unref(event);
500                 return FALSE;
501             }
502             break;
503         }
504         case GST_EVENT_FLUSH_START:
505             GST_INFO_OBJECT(vidShMemSink, "received FLUSH_START");
506             break;
507         case GST_EVENT_FLUSH_STOP:
508             g_mutex_lock(&priv->mutex);
509             GST_INFO_OBJECT(vidShMemSink, "received FLUSH_STOP");
510             gst_video_shmem_sink_flush_unlocked(vidShMemSink);
511             g_mutex_unlock(&priv->mutex);
512             break;
513         default:
514             break;
515     }
516     return GST_BASE_SINK_CLASS(parent_class)->event(bsink, event);
517 }
518 
gst_video_shmem_sink_query(GstBaseSink * bsink,GstQuery * query)519 static gboolean gst_video_shmem_sink_query(GstBaseSink *bsink, GstQuery *query)
520 {
521     GstVideoShMemSink *vidShMemSink = GST_VIDEO_SHMEM_SINK_CAST(bsink);
522     g_return_val_if_fail(vidShMemSink != nullptr, FALSE);
523     GstVideoShMemSinkPrivate *priv = vidShMemSink->priv;
524     g_return_val_if_fail(priv != nullptr, FALSE);
525 
526     gboolean ret;
527     switch (GST_QUERY_TYPE(query)) {
528         case GST_QUERY_DRAIN: {
529             if (!WaitBufferDrained(vidShMemSink)) {
530                 return FALSE;
531             }
532             ret = GST_BASE_SINK_CLASS(parent_class)->query(bsink, query);
533             break;
534         }
535         case GST_QUERY_SEEKING: {
536             GstFormat fmt;
537             gst_query_parse_seeking(query, &fmt, nullptr, nullptr, nullptr);
538             gst_query_set_seeking(query, fmt, FALSE, 0, -1);
539             ret = TRUE;
540             break;
541         }
542         default:
543             ret = GST_BASE_SINK_CLASS(parent_class)->query(bsink, query);
544             break;
545     }
546 
547     return ret;
548 }
549 
gst_video_shmem_sink_set_caps(GstBaseSink * sink,GstCaps * caps)550 static gboolean gst_video_shmem_sink_set_caps(GstBaseSink *sink, GstCaps *caps)
551 {
552     GstVideoShMemSink *vidShMemSink = GST_VIDEO_SHMEM_SINK_CAST(sink);
553     g_return_val_if_fail(vidShMemSink != nullptr, FALSE);
554     GstVideoShMemSinkPrivate *priv = vidShMemSink->priv;
555     g_return_val_if_fail(priv != nullptr, FALSE);
556 
557     g_mutex_lock(&priv->mutex);
558     GST_INFO_OBJECT(vidShMemSink, "receiving CAPS");
559     gst_queue_array_push_tail(priv->queue, gst_event_new_caps(caps));
560     if (priv->prerollBuffer == nullptr) {
561         (void)gst_caps_replace(&priv->prerollCaps, caps);
562     }
563     g_mutex_unlock(&priv->mutex);
564     return TRUE;
565 }
566 
gst_video_shmem_sink_get_caps(GstBaseSink * sink,GstCaps * filter)567 static GstCaps *gst_video_shmem_sink_get_caps(GstBaseSink *sink, GstCaps *filter)
568 {
569     GstVideoShMemSink *vidShMemSink = GST_VIDEO_SHMEM_SINK_CAST(sink);
570     g_return_val_if_fail(vidShMemSink != nullptr, nullptr);
571     GstVideoShMemSinkPrivate *priv = vidShMemSink->priv;
572     g_return_val_if_fail(priv != nullptr, nullptr);
573 
574     GST_OBJECT_LOCK(vidShMemSink);
575     GstCaps *caps = priv->caps;
576     if (caps != nullptr) {
577         if (filter != nullptr) {
578             caps = gst_caps_intersect_full(filter, caps, GST_CAPS_INTERSECT_FIRST);
579         } else {
580             (void)gst_caps_ref(caps);
581         }
582         GST_INFO_OBJECT(vidShMemSink, "got caps %s", gst_caps_to_string(caps));
583     }
584     GST_OBJECT_UNLOCK(vidShMemSink);
585 
586     return caps;
587 }
588 
gst_video_shmem_sink_pull_preroll(GstVideoShMemSink * vidShMemSink)589 static GstSample *gst_video_shmem_sink_pull_preroll(GstVideoShMemSink *vidShMemSink)
590 {
591     g_return_val_if_fail(vidShMemSink != nullptr, nullptr);
592     GstVideoShMemSinkPrivate *priv = vidShMemSink->priv;
593     g_return_val_if_fail(priv != nullptr, nullptr);
594 
595     GST_INFO_OBJECT(vidShMemSink, "pull preroll enter");
596     g_mutex_lock(&priv->mutex);
597 
598     while (TRUE) {
599         GST_INFO_OBJECT(vidShMemSink, "trying to grab a buffer");
600         if (!priv->started) {
601             GST_INFO_OBJECT(vidShMemSink, "we are stopped, return nullptr");
602             g_mutex_unlock(&priv->mutex);
603             return nullptr;
604         }
605         if (priv->prerollBuffer != nullptr) {
606             break;
607         }
608         if (priv->isEos) {
609             GST_INFO_OBJECT(vidShMemSink, "we are EOS, return nullptr");
610             g_mutex_unlock(&priv->mutex);
611             return nullptr;
612         }
613         GST_INFO_OBJECT(vidShMemSink, "waiting for the preroll buffer");
614         priv->waitStatus |= USER_WAITING;
615         g_cond_wait(&priv->cond, &priv->mutex);
616         priv->waitStatus &= ~USER_WAITING;
617     }
618 
619     GstSample *result = gst_sample_new(priv->prerollBuffer, priv->prerollCaps, &priv->prerollSegment, nullptr);
620     (void)gst_buffer_replace(&priv->prerollBuffer, nullptr);
621     GST_INFO_OBJECT(vidShMemSink, "we have the preroll sample 0x%06" PRIXPTR "", FAKE_POINTER(result));
622 
623     g_mutex_unlock(&priv->mutex);
624     return result;
625 }
626 
DequeueBuffer(GstVideoShMemSink * vidShMemSink)627 static GstBuffer *DequeueBuffer(GstVideoShMemSink *vidShMemSink)
628 {
629     GstVideoShMemSinkPrivate *priv = vidShMemSink->priv;
630     g_return_val_if_fail(priv != nullptr, nullptr);
631 
632     GstMiniObject *obj = nullptr;
633     GstBuffer *buffer = nullptr;
634     do {
635         obj = GST_MINI_OBJECT_CAST(gst_queue_array_pop_head(priv->queue));
636         if (GST_IS_BUFFER(obj)) {
637             GST_INFO_OBJECT(vidShMemSink, "dequeued buffer 0x%06" PRIXPTR "", FAKE_POINTER(obj));
638             priv->numBuffers--;
639             buffer = GST_BUFFER_CAST(obj);
640             break;
641         }
642 
643         if (GST_IS_EVENT(obj)) {
644             GstEvent *event = GST_EVENT_CAST(obj);
645             switch (GST_EVENT_TYPE(obj)) {
646                 case GST_EVENT_CAPS: {
647                     GstCaps *caps = nullptr;
648                     gst_event_parse_caps(event, &caps);
649                     GST_INFO_OBJECT(vidShMemSink, "activating caps %s", gst_caps_to_string(caps));
650                     (void)gst_caps_replace(&priv->lastCaps, caps);
651                     priv->sample = gst_sample_make_writable(priv->sample);
652                     gst_sample_set_caps(priv->sample, priv->lastCaps);
653                     break;
654                 }
655                 case GST_EVENT_SEGMENT: {
656                     gst_event_copy_segment(event, &priv->lastSegment);
657                     priv->sample = gst_sample_make_writable(priv->sample);
658                     gst_sample_set_segment(priv->sample, &priv->lastSegment);
659                     GST_INFO_OBJECT(vidShMemSink, "activated segment %" GST_SEGMENT_FORMAT, &priv->lastSegment);
660                     break;
661                 }
662                 default:
663                     break;
664             }
665             gst_mini_object_unref(obj);
666         }
667     } while (gst_queue_array_is_empty(priv->queue) == FALSE);
668 
669     return buffer;
670 }
671 
gst_video_shmem_sink_pull_sample(GstVideoShMemSink * vidShMemSink)672 static GstSample *gst_video_shmem_sink_pull_sample(GstVideoShMemSink *vidShMemSink)
673 {
674     g_return_val_if_fail(vidShMemSink != nullptr, nullptr);
675     GstVideoShMemSinkPrivate *priv = vidShMemSink->priv;
676     g_return_val_if_fail(priv != nullptr, nullptr);
677 
678     GST_INFO_OBJECT(vidShMemSink, "pull sample enter");
679     g_mutex_lock(&priv->mutex);
680     (void)gst_buffer_replace(&priv->prerollBuffer, nullptr);
681 
682     while (TRUE) {
683         GST_INFO_OBJECT(vidShMemSink, "trying to grab a buffer");
684         if (!priv->started) {
685             GST_INFO_OBJECT(vidShMemSink, "we are stopped, return nullptr");
686             g_mutex_unlock(&priv->mutex);
687             return nullptr;
688         }
689         if (priv->numBuffers > 0) {
690             break;
691         }
692         if (priv->isEos) {
693             GST_INFO_OBJECT(vidShMemSink, "we are EOS, return nullptr");
694             g_mutex_unlock(&priv->mutex);
695             return nullptr;
696         }
697         GST_INFO_OBJECT(vidShMemSink, "waiting for a buffer");
698         priv->waitStatus |= USER_WAITING;
699         g_cond_wait(&priv->cond, &priv->mutex);
700         priv->waitStatus &= ~USER_WAITING;
701     }
702 
703     GstBuffer *buffer = DequeueBuffer(vidShMemSink);
704     GST_INFO_OBJECT(vidShMemSink, "we have a buffer 0x%06" PRIXPTR "", FAKE_POINTER(buffer));
705     priv->sample = gst_sample_make_writable(priv->sample);
706     gst_sample_set_buffer_list(priv->sample, nullptr);
707     gst_sample_set_buffer(priv->sample, buffer);
708     GstSample *sample = gst_sample_ref(priv->sample);
709     gst_buffer_unref(buffer);
710 
711     g_mutex_unlock(&priv->mutex);
712     return sample;
713 }
714 
gst_video_shmem_sink_preroll(GstBaseSink * bsink,GstBuffer * buffer)715 static GstFlowReturn gst_video_shmem_sink_preroll(GstBaseSink *bsink, GstBuffer *buffer)
716 {
717     GstVideoShMemSink *vidShMemSink = GST_VIDEO_SHMEM_SINK_CAST(bsink);
718     g_return_val_if_fail(vidShMemSink != nullptr, GST_FLOW_ERROR);
719     GstVideoShMemSinkPrivate *priv = vidShMemSink->priv;
720     g_return_val_if_fail(priv != nullptr, GST_FLOW_ERROR);
721 
722     g_mutex_lock(&priv->mutex);
723 
724     if (priv->flushing) {
725         GST_INFO_OBJECT(vidShMemSink, "we are flushing");
726         g_mutex_unlock(&priv->mutex);
727         return GST_FLOW_FLUSHING;
728     }
729 
730     GST_INFO_OBJECT(vidShMemSink, "setting preroll buffer 0x%06" PRIXPTR "", FAKE_POINTER(buffer));
731     (void)gst_buffer_replace(&priv->prerollBuffer, buffer);
732 
733     if (priv->waitStatus & USER_WAITING) {
734         g_cond_signal(&priv->cond);
735     }
736 
737     g_mutex_unlock(&priv->mutex);
738 
739     GstFlowReturn ret = GST_FLOW_OK;
740     g_signal_emit(vidShMemSink, gst_video_shmem_sink_signals[SIGNAL_NEW_PREROLL], 0, &ret);
741 
742     return ret;
743 }
744 
gst_video_shmem_sink_render(GstBaseSink * bsink,GstBuffer * buffer)745 static GstFlowReturn gst_video_shmem_sink_render(GstBaseSink *bsink, GstBuffer *buffer)
746 {
747     GstVideoShMemSink *vidShMemSink = GST_VIDEO_SHMEM_SINK_CAST(bsink);
748     g_return_val_if_fail(vidShMemSink != nullptr, GST_FLOW_ERROR);
749     GstVideoShMemSinkPrivate *priv = vidShMemSink->priv;
750     g_return_val_if_fail(priv != nullptr, GST_FLOW_ERROR);
751 
752     g_mutex_lock(&priv->mutex);
753 
754     if (priv->flushing) {
755         GST_INFO_OBJECT(vidShMemSink, "we are flushing");
756         g_mutex_unlock(&priv->mutex);
757         return GST_FLOW_FLUSHING;
758     }
759 
760     if (G_UNLIKELY(priv->lastCaps == nullptr && gst_pad_has_current_caps(GST_BASE_SINK_PAD(bsink)))) {
761         priv->lastCaps = gst_pad_get_current_caps(GST_BASE_SINK_PAD(bsink));
762         gst_sample_set_caps(priv->sample, priv->lastCaps);
763         GST_INFO_OBJECT(vidShMemSink, "activating pad caps %s", gst_caps_to_string(priv->lastCaps));
764     }
765 
766     GST_INFO_OBJECT(vidShMemSink, "pushing render buffer 0x%06" PRIXPTR " on queue (%d)",
767                      FAKE_POINTER(buffer), priv->numBuffers);
768 
769     gst_queue_array_push_tail(priv->queue, gst_mini_object_ref(GST_MINI_OBJECT_CAST(buffer)));
770     priv->numBuffers++;
771 
772     if (priv->waitStatus & USER_WAITING) {
773         g_cond_signal(&priv->cond);
774     }
775 
776     g_mutex_unlock(&priv->mutex);
777 
778     GstFlowReturn ret = GST_FLOW_OK;
779     g_signal_emit(vidShMemSink, gst_video_shmem_sink_signals[SIGNAL_NEW_SAMPLE], 0, &ret);
780 
781     return ret;
782 }
783 
gst_video_shmem_sink_propose_allocation(GstBaseSink * bsink,GstQuery * query)784 static gboolean gst_video_shmem_sink_propose_allocation(GstBaseSink *bsink, GstQuery *query)
785 {
786     GstVideoShMemSink *vidShMemSink = GST_VIDEO_SHMEM_SINK_CAST(bsink);
787     g_return_val_if_fail(vidShMemSink != nullptr, FALSE);
788     g_return_val_if_fail(vidShMemSink->priv != nullptr, FALSE);
789 
790     GstCaps *caps = nullptr;
791     gboolean needPool = FALSE;
792     gst_query_parse_allocation(query, &caps, &needPool);
793     GST_DEBUG_OBJECT(bsink, "process allocation query, caps: %s", gst_caps_to_string(caps));
794 
795     if (!needPool) {
796         GST_ERROR_OBJECT(bsink, "no need buffer pool, unexpected!");
797         return FALSE;
798     }
799 
800     GstBufferPool *pool = gst_video_shmem_pool_new();
801     g_return_val_if_fail(pool != nullptr, FALSE);
802 
803     GstVideoInfo info;
804     GST_DEBUG("begin gst_video_info_from_caps");
805     gboolean ret = gst_video_info_from_caps(&info, caps);
806     if (!ret) {
807         gst_object_unref(pool);
808         GST_ERROR_OBJECT(vidShMemSink, "get video info from caps failed");
809         return ret;
810     }
811 
812     gst_query_add_allocation_pool(query, pool, info.size, 0, vidShMemSink->priv->maxPoolCapacity);
813 
814     GstAllocator *alloc = gst_shmem_allocator_old_new();
815     if (alloc == nullptr) {
816         gst_object_unref(pool);
817         GST_ERROR_OBJECT(vidShMemSink, "create shmem allocator failed");
818         return FALSE;
819     }
820 
821     GstAllocationParams allocParams {};
822     allocParams.prefix = vidShMemSink->priv->memPrefix;
823 
824     gst_query_add_allocation_param(query, alloc, &allocParams);
825     gst_query_add_allocation_meta(query, GST_VIDEO_META_API_TYPE, nullptr);
826 
827     GstStructure *config = gst_buffer_pool_get_config(pool);
828     gst_buffer_pool_config_set_params(config, caps, info.size, 0, vidShMemSink->priv->maxPoolCapacity);
829     gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_VIDEO_META);
830     gst_buffer_pool_config_set_allocator(config, alloc, &allocParams);
831     ret = gst_buffer_pool_set_config(pool, config);
832     gst_object_unref(pool);
833     gst_object_unref(alloc);
834 
835     g_return_val_if_fail(ret, FALSE);
836     return TRUE;
837 }
838 
plugin_init(GstPlugin * plugin)839 static gboolean plugin_init(GstPlugin *plugin)
840 {
841     g_return_val_if_fail(plugin != nullptr, false);
842     return gst_element_register(plugin, "vidshmemsink", GST_RANK_MARGINAL, GST_TYPE_VIDEO_SHMEM_SINK);
843 }
844 
845 GST_PLUGIN_DEFINE(GST_VERSION_MAJOR,
846     GST_VERSION_MINOR,
847     _video_shmem_sink,
848     "GStreamer Video ShMem Sink",
849     plugin_init,
850     PACKAGE_VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
851