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