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