• 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_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, &params);
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, &params);
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