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_shmem_pool.h"
17 #include "gst/video/gstvideometa.h"
18 #include "gst_shmem_allocator.h"
19 #include "media_log.h"
20 #include "media_errors.h"
21 #include "buffer_type_meta.h"
22 #include "avsharedmemorybase.h"
23
24 #define GST_BUFFER_POOL_LOCK(pool) (g_mutex_lock(&pool->lock))
25 #define GST_BUFFER_POOL_UNLOCK(pool) (g_mutex_unlock(&pool->lock))
26
27 #define gst_shmem_pool_parent_class parent_class
28 G_DEFINE_TYPE(GstShMemPool, gst_shmem_pool, GST_TYPE_BUFFER_POOL);
29
30 static void gst_shmem_pool_finalize(GObject *obj);
31 static const gchar **gst_shmem_pool_get_options(GstBufferPool *pool);
32 static gboolean gst_shmem_pool_set_config(GstBufferPool *pool, GstStructure *config);
33 static gboolean gst_shmem_pool_start(GstBufferPool *pool);
34 static gboolean gst_shmem_pool_stop(GstBufferPool *pool);
35 static GstFlowReturn gst_shmem_pool_alloc_buffer(GstBufferPool *pool,
36 GstBuffer **buffer, GstBufferPoolAcquireParams *params);
37 static void gst_shmem_pool_free_buffer(GstBufferPool *pool, GstBuffer *buffer);
38 static GstFlowReturn gst_shmem_pool_acquire_buffer(GstBufferPool *pool,
39 GstBuffer **buffer, GstBufferPoolAcquireParams *params);
40 static void gst_shmem_pool_release_buffer(GstBufferPool *pool, GstBuffer *buffer);
41 static void gst_shmem_pool_memory_available(GstBufferPool *pool);
42
43 GST_DEBUG_CATEGORY_STATIC(gst_shmem_pool_debug_category);
44 #define GST_CAT_DEFAULT gst_shmem_pool_debug_category
45
gst_shmem_pool_class_init(GstShMemPoolClass * klass)46 static void gst_shmem_pool_class_init(GstShMemPoolClass *klass)
47 {
48 g_return_if_fail(klass != nullptr);
49 GstBufferPoolClass *poolClass = GST_BUFFER_POOL_CLASS(klass);
50 GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
51
52 gobjectClass->finalize = gst_shmem_pool_finalize;
53 poolClass->get_options = gst_shmem_pool_get_options;
54 poolClass->set_config = gst_shmem_pool_set_config;
55 poolClass->start = gst_shmem_pool_start;
56 poolClass->stop = gst_shmem_pool_stop;
57 poolClass->acquire_buffer = gst_shmem_pool_acquire_buffer;
58 poolClass->alloc_buffer = gst_shmem_pool_alloc_buffer;
59 poolClass->release_buffer = gst_shmem_pool_release_buffer;
60 poolClass->free_buffer = gst_shmem_pool_free_buffer;
61
62 GST_DEBUG_CATEGORY_INIT(gst_shmem_pool_debug_category, "shmempool", 0, "shmempool class");
63 }
64
gst_shmem_pool_init(GstShMemPool * pool)65 static void gst_shmem_pool_init(GstShMemPool *pool)
66 {
67 g_return_if_fail(pool != nullptr);
68
69 pool->started = FALSE;
70 g_mutex_init(&pool->lock);
71 g_cond_init(&pool->cond);
72 pool->allocator = nullptr;
73 pool->minBuffers = 0;
74 pool->maxBuffers = 0;
75 pool->avshmempool = nullptr;
76 pool->addVideoMeta = FALSE;
77 gst_video_info_init(&pool->info);
78 pool->size = 0;
79 pool->curBuffers = 0;
80 pool->end = FALSE;
81 pool->debugName = g_strdup("");
82 gst_allocation_params_init(&pool->params);
83
84 GST_DEBUG_OBJECT(pool, "init pool");
85 }
86
gst_shmem_pool_finalize(GObject * obj)87 static void gst_shmem_pool_finalize(GObject *obj)
88 {
89 g_return_if_fail(obj != nullptr);
90 GstShMemPool *spool = GST_SHMEM_POOL_CAST(obj);
91 g_return_if_fail(spool != nullptr);
92
93 if (spool->allocator != nullptr) {
94 gst_object_unref(spool->allocator);
95 spool->allocator = nullptr;
96 }
97
98 g_mutex_clear(&spool->lock);
99 g_cond_clear(&spool->cond);
100 if (spool->avshmempool != nullptr) {
101 spool->avshmempool->Reset();
102 spool->avshmempool = nullptr;
103 }
104 spool->end = TRUE;
105 if (spool->debugName != nullptr) {
106 g_free(spool->debugName);
107 spool->debugName = nullptr;
108 }
109
110 GST_DEBUG_OBJECT(spool, "finalize pool");
111 G_OBJECT_CLASS(parent_class)->finalize(obj);
112 }
113
gst_shmem_pool_new()114 GstShMemPool *gst_shmem_pool_new()
115 {
116 GstShMemPool *pool = GST_SHMEM_POOL_CAST(g_object_new(
117 GST_TYPE_SHMEM_POOL, "name", "ShMemPool", nullptr));
118 (void)gst_object_ref_sink(pool);
119
120 return pool;
121 }
122
gst_shmem_pool_get_options(GstBufferPool * pool)123 static const gchar **gst_shmem_pool_get_options(GstBufferPool *pool)
124 {
125 // add buffer type meta option at here
126 (void)pool;
127 static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META, nullptr };
128 return options;
129 }
130
gst_shmem_pool_set_avshmempool(GstShMemPool * pool,const std::shared_ptr<OHOS::Media::AVSharedMemoryPool> & avshmempool)131 gboolean gst_shmem_pool_set_avshmempool(GstShMemPool *pool,
132 const std::shared_ptr<OHOS::Media::AVSharedMemoryPool> &avshmempool)
133 {
134 g_return_val_if_fail(pool != nullptr && avshmempool != nullptr, FALSE);
135
136 GST_BUFFER_POOL_LOCK(pool);
137
138 if (pool->started) {
139 GST_ERROR("started already, reject to set avshmempool");
140 GST_BUFFER_POOL_UNLOCK(pool);
141 return FALSE;
142 }
143
144 if (pool->avshmempool != nullptr) {
145 GST_ERROR("avshmempool has already been set");
146 GST_BUFFER_POOL_UNLOCK(pool);
147 return FALSE;
148 }
149 pool->avshmempool = avshmempool;
150
151 if (pool->debugName != nullptr) {
152 g_free(pool->debugName);
153 pool->debugName = g_strdup(avshmempool->GetName().c_str());
154 }
155
156 GST_BUFFER_POOL_UNLOCK(pool);
157 return TRUE;
158 }
159
parse_caps_for_raw_video(GstShMemPool * spool,GstCaps * caps,guint * size)160 static gboolean parse_caps_for_raw_video(GstShMemPool *spool, GstCaps *caps, guint *size)
161 {
162 GstStructure *structure = gst_caps_get_structure(caps, 0);
163 g_return_val_if_fail(structure != nullptr, FALSE);
164 if (gst_structure_has_name(structure, "video/x-raw")) {
165 GstVideoInfo info;
166 gboolean ret = gst_video_info_from_caps(&info, caps);
167 g_return_val_if_fail(ret, FALSE);
168 *size = info.size > *size ? info.size : *size;
169 GST_INFO("this is video raw scene");
170 spool->info = info;
171 spool->addVideoMeta = TRUE;
172 return TRUE;
173 }
174
175 return FALSE;
176 }
177
gst_shmem_pool_set_config(GstBufferPool * pool,GstStructure * config)178 static gboolean gst_shmem_pool_set_config(GstBufferPool *pool, GstStructure *config)
179 {
180 GstShMemPool *spool = GST_SHMEM_POOL_CAST(pool);
181 g_return_val_if_fail(spool != nullptr, FALSE);
182 g_return_val_if_fail(config != nullptr, FALSE);
183
184 GstCaps *caps = nullptr;
185 guint size;
186 guint minBuffers;
187 guint maxBuffers;
188 static constexpr guint defaultMaxBuffers = 10;
189 if (!gst_buffer_pool_config_get_params(config, &caps, &size, &minBuffers, &maxBuffers)) {
190 GST_ERROR("wrong config");
191 return FALSE;
192 }
193 g_return_val_if_fail(minBuffers <= maxBuffers, FALSE);
194
195 GST_BUFFER_POOL_LOCK(spool);
196
197 spool->minBuffers = minBuffers;
198 spool->maxBuffers = maxBuffers > 0 ? maxBuffers : defaultMaxBuffers;
199 parse_caps_for_raw_video(spool, caps, &size);
200 spool->size = size;
201
202 GstAllocator *allocator = nullptr;
203 GstAllocationParams params;
204 (void)gst_buffer_pool_config_get_allocator(config, &allocator, ¶ms);
205 if (allocator == nullptr) {
206 GST_WARNING_OBJECT(pool, "allocator is null");
207 }
208 if (!(allocator != nullptr && GST_IS_SHMEM_ALLOCATOR(allocator))) {
209 GST_BUFFER_POOL_UNLOCK(spool);
210 GST_WARNING_OBJECT(pool, "no valid allocator in pool");
211 return FALSE;
212 }
213
214 GST_INFO("set config, size: %u, min_bufs: %u, max_bufs: %u", size, minBuffers, maxBuffers);
215
216 if (spool->allocator != nullptr) {
217 gst_object_unref(spool->allocator);
218 }
219 spool->allocator = GST_SHMEM_ALLOCATOR_CAST(gst_object_ref(allocator));
220 spool->params = params;
221
222 GST_BUFFER_POOL_UNLOCK(spool);
223
224 // modify the minBuffers to zero, we preallocate buffer by the avshmempool's memory available notifier.
225 gst_buffer_pool_config_set_params(config, caps, size, 0, maxBuffers);
226 GST_BUFFER_POOL_CLASS(parent_class)->set_config(pool, config);
227 return TRUE;
228 }
229
gst_shmem_pool_start(GstBufferPool * pool)230 static gboolean gst_shmem_pool_start(GstBufferPool *pool)
231 {
232 g_return_val_if_fail(pool != nullptr, FALSE);
233 GstShMemPool *spool = GST_SHMEM_POOL_CAST(pool);
234 g_return_val_if_fail(spool != nullptr, FALSE);
235
236 GST_DEBUG("pool start");
237
238 GST_BUFFER_POOL_LOCK(spool);
239 if (spool->avshmempool == nullptr) {
240 GST_BUFFER_POOL_UNLOCK(spool);
241 GST_ERROR("not set avshmempool");
242 return FALSE;
243 }
244
245 // clear the configuration carried in the avshmempool
246 spool->avshmempool->Reset();
247 auto notifier = [pool]() {
248 gst_shmem_pool_memory_available(pool);
249 };
250
251 static constexpr uint32_t alignBytes = 4;
252 gsize alignedPrefix = (spool->params.prefix + alignBytes - 1) & ~(alignBytes - 1);
253 OHOS::Media::AVSharedMemoryPool::InitializeOption option = {
254 .preAllocMemCnt = spool->minBuffers,
255 .memSize = spool->size + alignedPrefix,
256 .maxMemCnt = spool->maxBuffers,
257 .notifier = notifier,
258 };
259
260 int32_t ret = spool->avshmempool->Init(option);
261 if (ret != OHOS::Media::MSERR_OK) {
262 GST_BUFFER_POOL_UNLOCK(spool);
263 GST_ERROR("init avshmempool failed, ret = %d", ret);
264 return FALSE;
265 }
266
267 // Always force to non-blocking way to alloc shared memory if we use the gstbuferpool's memory manage.
268 spool->avshmempool->SetNonBlocking(true);
269 gst_shmem_allocator_set_pool(spool->allocator, spool->avshmempool);
270
271 gboolean rc = GST_BUFFER_POOL_CLASS(parent_class)->start(pool);
272 if (!rc) {
273 GST_BUFFER_POOL_UNLOCK(spool);
274 GST_ERROR("parent class start failed");
275 return rc;
276 }
277
278 spool->started = TRUE;
279 GST_BUFFER_POOL_UNLOCK(spool);
280 return TRUE;
281 }
282
gst_shmem_pool_stop(GstBufferPool * pool)283 static gboolean gst_shmem_pool_stop(GstBufferPool *pool)
284 {
285 g_return_val_if_fail(pool != nullptr, FALSE);
286 GstShMemPool *spool = GST_SHMEM_POOL_CAST(pool);
287 g_return_val_if_fail(spool != nullptr, FALSE);
288 if (spool->end) {
289 return GST_BUFFER_POOL_CLASS(parent_class)->stop(pool);
290 }
291
292 GST_DEBUG("pool stop");
293 GST_BUFFER_POOL_LOCK(spool);
294 spool->started = FALSE;
295 if (spool->avshmempool != nullptr) {
296 spool->avshmempool->Reset();
297 }
298
299 gboolean ret = GST_BUFFER_POOL_CLASS(parent_class)->stop(pool);
300 GST_DEBUG("parent class stop ret: %d, ret", ret);
301 ret = ret && (g_atomic_int_get(&spool->curBuffers) == 0);
302 GST_DEBUG("stop pool, curBuffers: %d, maxBuffers: %u", spool->curBuffers, spool->maxBuffers);
303
304 // leave all configuration unchanged.
305 GST_BUFFER_POOL_UNLOCK(spool);
306 return ret;
307 }
308
add_meta_to_buffer(GstShMemPool * spool,GstBuffer * buffer,GstShMemMemory * memory)309 static GstFlowReturn add_meta_to_buffer(GstShMemPool *spool, GstBuffer *buffer, GstShMemMemory *memory)
310 {
311 if (spool->addVideoMeta) {
312 GstVideoInfo *info = &spool->info;
313 gst_buffer_add_video_meta(buffer, GST_VIDEO_FRAME_FLAG_NONE, GST_VIDEO_INFO_FORMAT(info),
314 GST_VIDEO_INFO_WIDTH(info), GST_VIDEO_INFO_HEIGHT(info));
315 }
316
317 auto mem = std::static_pointer_cast<OHOS::Media::AVSharedMemoryBase>(memory->mem);
318 if (mem == nullptr) {
319 GST_ERROR("invalid pointer");
320 return GST_FLOW_ERROR;
321 }
322
323 AVShmemFlags flag = FLAGS_READ_WRITE;
324 if (mem->GetFlags() == OHOS::Media::AVSharedMemory::FLAGS_READ_ONLY) {
325 flag = FLAGS_READ_ONLY;
326 }
327
328 GstBufferFdConfig config = { sizeof(mem->GetFd()), 0, mem->GetSize(), mem->GetSize(), flag, 0 };
329 gst_buffer_add_buffer_fd_meta(buffer, mem->GetFd(), config);
330
331 return GST_FLOW_OK;
332 }
333
gst_shmem_pool_alloc_buffer(GstBufferPool * pool,GstBuffer ** buffer,GstBufferPoolAcquireParams * params)334 static GstFlowReturn gst_shmem_pool_alloc_buffer(GstBufferPool *pool,
335 GstBuffer **buffer, GstBufferPoolAcquireParams *params)
336 {
337 (void)params;
338 g_return_val_if_fail(pool != nullptr && buffer != nullptr, GST_FLOW_ERROR);
339 GstShMemPool *spool = GST_SHMEM_POOL_CAST(pool);
340 g_return_val_if_fail(spool != nullptr, GST_FLOW_ERROR);
341 GstFlowReturn ret = GST_FLOW_OK;
342
343 GstShMemMemory *memory = reinterpret_cast<GstShMemMemory *>(
344 gst_allocator_alloc(GST_ALLOCATOR_CAST(spool->allocator), spool->size, &spool->params));
345 if (memory == nullptr) {
346 GST_DEBUG("alloc memory failed");
347 return GST_FLOW_EOS;
348 }
349
350 *buffer = gst_buffer_new();
351 if (*buffer == nullptr) {
352 GST_ERROR("alloc gst buffer failed");
353 gst_allocator_free(GST_ALLOCATOR_CAST(spool->allocator), reinterpret_cast<GstMemory *>(memory));
354 return GST_FLOW_ERROR;
355 }
356
357 gst_buffer_append_memory(*buffer, reinterpret_cast<GstMemory *>(memory));
358
359 ret = add_meta_to_buffer(spool, *buffer, memory);
360 if (ret != GST_FLOW_OK) {
361 gst_buffer_unref(*buffer);
362 return ret;
363 }
364
365 GST_LOG("alloc buffer ok, 0x%06" PRIXPTR " from pool %s", FAKE_POINTER(*buffer), spool->debugName);
366 g_atomic_int_add(&spool->curBuffers, 1);
367 return GST_FLOW_OK;
368 }
369
gst_shmem_pool_free_buffer(GstBufferPool * pool,GstBuffer * buffer)370 static void gst_shmem_pool_free_buffer(GstBufferPool *pool, GstBuffer *buffer)
371 {
372 g_return_if_fail(pool != nullptr);
373 GstShMemPool *spool = GST_SHMEM_POOL_CAST(pool);
374 g_return_if_fail(buffer != nullptr);
375
376 gst_buffer_unref(buffer);
377 g_atomic_int_add(&spool->curBuffers, -1);
378 GST_LOG("free buffer ok, 0x%06" PRIXPTR ", from pool %s", FAKE_POINTER(buffer), spool->debugName);
379 }
380
gst_shmem_pool_acquire_buffer(GstBufferPool * pool,GstBuffer ** buffer,GstBufferPoolAcquireParams * params)381 static GstFlowReturn gst_shmem_pool_acquire_buffer(GstBufferPool *pool,
382 GstBuffer **buffer, GstBufferPoolAcquireParams *params)
383 {
384 g_return_val_if_fail(pool != nullptr && buffer != nullptr, GST_FLOW_ERROR);
385 GstShMemPool *spool = GST_SHMEM_POOL_CAST(pool);
386 g_return_val_if_fail(spool != nullptr, GST_FLOW_ERROR);
387
388 GST_LOG("acquire buffer from pool 0x%06" PRIXPTR " %s before", FAKE_POINTER(pool), spool->debugName);
389 GstFlowReturn ret = GST_BUFFER_POOL_CLASS(parent_class)->acquire_buffer(pool, buffer, params);
390
391 // The GstBufferPool will add the GstBuffer's pool ref to this pool.
392 GST_LOG("acquire buffer from pool 0x%06" PRIXPTR " %s after", FAKE_POINTER(pool), spool->debugName);
393 return ret;
394 }
395
gst_shmem_pool_release_buffer(GstBufferPool * pool,GstBuffer * buffer)396 static void gst_shmem_pool_release_buffer(GstBufferPool *pool, GstBuffer *buffer)
397 {
398 // The GstBufferPool has already cleared the GstBuffer's pool ref to this pool.
399 GstShMemPool *spool = GST_SHMEM_POOL_CAST(pool);
400 g_return_if_fail(spool != nullptr);
401 GST_LOG("release buffer 0x%06" PRIXPTR " to pool 0x%06" PRIXPTR " %s",
402 FAKE_POINTER(buffer), FAKE_POINTER(pool), spool->debugName);
403
404 GST_BUFFER_POOL_CLASS(parent_class)->release_buffer(pool, buffer);
405 }
406
gst_shmem_pool_memory_available(GstBufferPool * pool)407 static void gst_shmem_pool_memory_available(GstBufferPool *pool)
408 {
409 g_return_if_fail(pool != nullptr);
410
411 GstShMemPool *spool = GST_SHMEM_POOL_CAST(pool);
412 GST_DEBUG("pool memory available, currBuffers: %d", g_atomic_int_get(&spool->curBuffers));
413
414 GstBuffer *buffer = nullptr;
415 GstBufferPoolAcquireParams params = { GST_FORMAT_DEFAULT, 0, 0, GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT, {} };
416 GstFlowReturn ret = gst_shmem_pool_acquire_buffer(pool, &buffer, ¶ms);
417
418 GST_DEBUG("memory available, fake acquire ret: %s", gst_flow_get_name(ret));
419 // Get buffer maybe fail f there ary any others getting buffer concurringly.
420 if (ret != GST_FLOW_OK) {
421 return;
422 }
423
424 gst_shmem_pool_release_buffer(pool, buffer);
425 }
426