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_consumer_surface_pool.h"
17 #include "gst_consumer_surface_allocator.h"
18 #include "gst_consumer_surface_memory.h"
19 #include "buffer_type_meta.h"
20 #include "scope_guard.h"
21 using namespace OHOS;
22
23 #define gst_consumer_surface_pool_parent_class parent_class
24
25 GST_DEBUG_CATEGORY_STATIC(gst_consumer_surface_pool_debug_category);
26 #define GST_CAT_DEFAULT gst_consumer_surface_pool_debug_category
27
28 struct _GstConsumerSurfacePoolPrivate {
29 sptr<Surface> consumer_surface;
30 guint available_buf_count;
31 GMutex pool_lock;
32 GCond buffer_available_con;
33 gboolean flushing;
34 gboolean start;
35 gboolean suspend;
36 gboolean is_first_buffer;
37 guint32 repeat_interval;
38 guint32 max_frame_rate;
39 guint64 pre_timestamp;
40 GstBuffer *cache_buffer;
41 };
42
43 enum {
44 PROP_0,
45 PROP_SUSPEND,
46 PROP_REPEAT,
47 PROP_MAX_FRAME_RATE,
48 };
49
50 G_DEFINE_TYPE_WITH_PRIVATE(GstConsumerSurfacePool, gst_consumer_surface_pool, GST_TYPE_VIDEO_BUFFER_POOL);
51
52 class ConsumerListenerProxy : public IBufferConsumerListener, public NoCopyable {
53 public:
ConsumerListenerProxy(GstConsumerSurfacePool & owner)54 explicit ConsumerListenerProxy(GstConsumerSurfacePool &owner) : owner_(owner) {}
55 ~ConsumerListenerProxy() = default;
56 void OnBufferAvailable() override;
57 private:
58 GstConsumerSurfacePool &owner_;
59 };
60
61 static void gst_consumer_surface_pool_set_property(GObject *object, guint id, const GValue *value, GParamSpec *pspec);
62 static void gst_consumer_surface_pool_init(GstConsumerSurfacePool *pool);
63 static void gst_consumer_surface_pool_buffer_available(GstConsumerSurfacePool *pool);
64 static GstFlowReturn gst_consumer_surface_pool_acquire_buffer(GstBufferPool *pool, GstBuffer **buffer,
65 GstBufferPoolAcquireParams *params);
66 static void gst_consumer_surface_pool_release_buffer(GstBufferPool *pool, GstBuffer *buffer);
67 static gboolean gst_consumer_surface_pool_stop(GstBufferPool *pool);
68 static gboolean gst_consumer_surface_pool_start(GstBufferPool *pool);
69 static void gst_consumer_surface_pool_flush_start(GstBufferPool *pool);
70 static void gst_consumer_surface_pool_flush_stop(GstBufferPool *pool);
71 static void add_buffer_info(GstConsumerSurfacePool *pool, GstConsumerSurfaceMemory *mem, GstBuffer *buffer);
72 static void cache_frame_if_necessary(GstConsumerSurfacePool *pool, GstConsumerSurfaceMemory *mem, GstBuffer *buffer);
73 static gboolean drop_this_frame(GstConsumerSurfacePool *pool, guint64 new_timestamp,
74 guint64 old_timestamp, guint32 frame_rate);
75
OnBufferAvailable()76 void ConsumerListenerProxy::OnBufferAvailable()
77 {
78 gst_consumer_surface_pool_buffer_available(&owner_);
79 }
80
gst_consumer_surface_pool_get_options(GstBufferPool * pool)81 static const gchar **gst_consumer_surface_pool_get_options(GstBufferPool *pool)
82 {
83 static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META, nullptr };
84 return options;
85 }
86
gst_consumer_surface_pool_set_config(GstBufferPool * pool,GstStructure * config)87 static gboolean gst_consumer_surface_pool_set_config(GstBufferPool *pool, GstStructure *config)
88 {
89 g_return_val_if_fail(pool != nullptr, FALSE);
90 g_return_val_if_fail(config != nullptr, FALSE);
91
92 GstAllocator *allocator = nullptr;
93 (void)gst_buffer_pool_config_get_allocator(config, &allocator, nullptr);
94 if (!(allocator && GST_IS_CONSUMER_SURFACE_ALLOCATOR(allocator))) {
95 GST_WARNING_OBJECT(pool, "no valid allocator in pool");
96 return FALSE;
97 }
98
99 return GST_BUFFER_POOL_CLASS(parent_class)->set_config(pool, config);
100 }
101
102 // before unref must stop(deactive)
gst_consumer_surface_pool_finalize(GObject * obj)103 static void gst_consumer_surface_pool_finalize(GObject *obj)
104 {
105 g_return_if_fail(obj != nullptr);
106 GstConsumerSurfacePool *surfacepool = GST_CONSUMER_SURFACE_POOL_CAST(obj);
107 g_return_if_fail(surfacepool != nullptr && surfacepool->priv != nullptr);
108 auto priv = surfacepool->priv;
109 if (priv->consumer_surface != nullptr) {
110 if (priv->consumer_surface->UnregisterConsumerListener() != SURFACE_ERROR_OK) {
111 GST_WARNING_OBJECT(surfacepool, "deregister consumer listener fail");
112 }
113 priv->consumer_surface = nullptr;
114 }
115 g_mutex_clear(&priv->pool_lock);
116 g_cond_clear(&priv->buffer_available_con);
117 G_OBJECT_CLASS(parent_class)->finalize(obj);
118 }
119
gst_consumer_surface_pool_class_init(GstConsumerSurfacePoolClass * klass)120 static void gst_consumer_surface_pool_class_init(GstConsumerSurfacePoolClass *klass)
121 {
122 g_return_if_fail(klass != nullptr);
123 GstBufferPoolClass *poolClass = GST_BUFFER_POOL_CLASS (klass);
124 GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
125 GST_DEBUG_CATEGORY_INIT(gst_consumer_surface_pool_debug_category, "surfacepool", 0, "surface pool");
126 gobjectClass->set_property = gst_consumer_surface_pool_set_property;
127 gobjectClass->finalize = gst_consumer_surface_pool_finalize;
128
129 g_object_class_install_property(gobjectClass, PROP_SUSPEND,
130 g_param_spec_boolean("suspend", "Suspend surface", "Suspend surface",
131 FALSE, (GParamFlags)(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)));
132
133 g_object_class_install_property(gobjectClass, PROP_REPEAT,
134 g_param_spec_uint("repeat", "Repeat frame", "Repeat previous frame after given milliseconds",
135 0, G_MAXUINT32, 0, (GParamFlags)(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)));
136
137 g_object_class_install_property(gobjectClass, PROP_MAX_FRAME_RATE,
138 g_param_spec_uint("max-framerate", "Max frame rate", "Max frame rate",
139 0, G_MAXUINT32, 0, (GParamFlags)(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)));
140
141 poolClass->get_options = gst_consumer_surface_pool_get_options;
142 poolClass->set_config = gst_consumer_surface_pool_set_config;
143 poolClass->release_buffer = gst_consumer_surface_pool_release_buffer;
144 poolClass->acquire_buffer = gst_consumer_surface_pool_acquire_buffer;
145 poolClass->start = gst_consumer_surface_pool_start;
146 poolClass->stop = gst_consumer_surface_pool_stop;
147 poolClass->flush_start = gst_consumer_surface_pool_flush_start;
148 poolClass->flush_stop = gst_consumer_surface_pool_flush_stop;
149 }
150
gst_consumer_surface_pool_set_property(GObject * object,guint id,const GValue * value,GParamSpec * pspec)151 static void gst_consumer_surface_pool_set_property(GObject *object, guint id, const GValue *value, GParamSpec *pspec)
152 {
153 (void)pspec;
154 GstConsumerSurfacePool *surfacepool = GST_CONSUMER_SURFACE_POOL(object);
155 g_return_if_fail(surfacepool != nullptr && value != nullptr);
156 auto priv = surfacepool->priv;
157
158 g_mutex_lock(&priv->pool_lock);
159 ON_SCOPE_EXIT(0) { g_mutex_unlock(&priv->pool_lock); };
160
161 switch (id) {
162 case PROP_SUSPEND:
163 priv->suspend = g_value_get_boolean(value);
164 break;
165 case PROP_REPEAT:
166 if (g_value_get_uint(value) == 0 && priv->cache_buffer != nullptr) {
167 gst_buffer_unref(priv->cache_buffer);
168 priv->cache_buffer = nullptr;
169 }
170 priv->repeat_interval = g_value_get_uint(value) * 1000; // ms to us
171 break;
172 case PROP_MAX_FRAME_RATE:
173 priv->max_frame_rate = g_value_get_uint(value);
174 break;
175 default:
176 break;
177 }
178 }
179
gst_consumer_surface_pool_flush_start(GstBufferPool * pool)180 static void gst_consumer_surface_pool_flush_start(GstBufferPool *pool)
181 {
182 GstConsumerSurfacePool *surfacepool = GST_CONSUMER_SURFACE_POOL(pool);
183 g_return_if_fail(surfacepool != nullptr && surfacepool->priv != nullptr);
184 auto priv = surfacepool->priv;
185 g_mutex_lock(&priv->pool_lock);
186 if (priv->cache_buffer != nullptr) {
187 gst_buffer_unref(priv->cache_buffer);
188 priv->cache_buffer = nullptr;
189 }
190 surfacepool->priv->flushing = TRUE;
191 g_cond_signal(&priv->buffer_available_con);
192 g_mutex_unlock(&priv->pool_lock);
193 }
194
gst_consumer_surface_pool_flush_stop(GstBufferPool * pool)195 static void gst_consumer_surface_pool_flush_stop(GstBufferPool *pool)
196 {
197 GstConsumerSurfacePool *surfacepool = GST_CONSUMER_SURFACE_POOL(pool);
198 g_return_if_fail(surfacepool != nullptr && surfacepool->priv != nullptr);
199 auto priv = surfacepool->priv;
200 g_mutex_lock(&priv->pool_lock);
201 surfacepool->priv->flushing = FALSE;
202 surfacepool->priv->is_first_buffer = TRUE;
203 g_mutex_unlock(&priv->pool_lock);
204 }
205
206 // Disable pre-caching
gst_consumer_surface_pool_start(GstBufferPool * pool)207 static gboolean gst_consumer_surface_pool_start(GstBufferPool *pool)
208 {
209 GstConsumerSurfacePool *surfacepool = GST_CONSUMER_SURFACE_POOL(pool);
210 g_return_val_if_fail(surfacepool != nullptr && surfacepool->priv != nullptr, FALSE);
211 auto priv = surfacepool->priv;
212 g_mutex_lock(&priv->pool_lock);
213 surfacepool->priv->start = TRUE;
214 g_mutex_unlock(&priv->pool_lock);
215 return TRUE;
216 }
217
218 // Disable release buffers
gst_consumer_surface_pool_stop(GstBufferPool * pool)219 static gboolean gst_consumer_surface_pool_stop(GstBufferPool *pool)
220 {
221 GstConsumerSurfacePool *surfacepool = GST_CONSUMER_SURFACE_POOL(pool);
222 g_return_val_if_fail(surfacepool != nullptr && surfacepool->priv != nullptr, FALSE);
223 auto priv = surfacepool->priv;
224 g_mutex_lock(&priv->pool_lock);
225 surfacepool->priv->start = FALSE;
226 g_cond_signal(&priv->buffer_available_con);
227 g_mutex_unlock(&priv->pool_lock);
228 return TRUE;
229 }
230
gst_consumer_surface_pool_release_buffer(GstBufferPool * pool,GstBuffer * buffer)231 static void gst_consumer_surface_pool_release_buffer(GstBufferPool *pool, GstBuffer *buffer)
232 {
233 g_return_if_fail(pool != nullptr && buffer != nullptr);
234 GstMemory *mem = gst_buffer_peek_memory(buffer, 0);
235 if (gst_is_consumer_surface_memory(mem)) {
236 GstBufferTypeMeta *meta = gst_buffer_get_buffer_type_meta(buffer);
237 if (meta != nullptr) {
238 GstConsumerSurfaceMemory *surfacemem = reinterpret_cast<GstConsumerSurfaceMemory *>(mem);
239 surfacemem->fencefd = meta->fenceFd;
240 }
241 }
242 // the buffer's pool is remove, the buffer will free by allocator.
243 gst_buffer_unref(buffer);
244 }
245
gst_consumer_surface_pool_acquire_buffer(GstBufferPool * pool,GstBuffer ** buffer,GstBufferPoolAcquireParams * params)246 static GstFlowReturn gst_consumer_surface_pool_acquire_buffer(GstBufferPool *pool, GstBuffer **buffer,
247 GstBufferPoolAcquireParams *params)
248 {
249 GstConsumerSurfacePool *surfacepool = GST_CONSUMER_SURFACE_POOL(pool);
250 g_return_val_if_fail(surfacepool != nullptr && surfacepool->priv != nullptr, GST_FLOW_ERROR);
251 GstBufferPoolClass *pclass = GST_BUFFER_POOL_GET_CLASS(pool);
252 g_return_val_if_fail(pclass != nullptr, GST_FLOW_ERROR);
253 if (!pclass->alloc_buffer) {
254 return GST_FLOW_NOT_SUPPORTED;
255 }
256 auto priv = surfacepool->priv;
257 g_mutex_lock(&priv->pool_lock);
258 ON_SCOPE_EXIT(0) { g_mutex_unlock(&priv->pool_lock); };
259
260 while (true) {
261 gboolean repeat = FALSE;
262 while (priv->available_buf_count == 0 && !priv->flushing && priv->start) {
263 if (priv->repeat_interval == 0 || priv->cache_buffer == nullptr) {
264 g_cond_wait(&priv->buffer_available_con, &priv->pool_lock);
265 } else if (g_cond_wait_until(&priv->buffer_available_con, &priv->pool_lock, priv->repeat_interval)) {
266 GST_INFO_OBJECT(surfacepool, "Repeat previous frame after waiting given microseconds");
267 repeat = TRUE;
268 }
269 }
270 if (priv->flushing || !priv->start) {
271 return GST_FLOW_FLUSHING;
272 }
273
274 if (repeat && priv->cache_buffer != nullptr) {
275 *buffer = priv->cache_buffer;
276 gst_buffer_ref(priv->cache_buffer);
277 GST_BUFFER_PTS(*buffer) = priv->pre_timestamp + priv->repeat_interval;
278 priv->pre_timestamp = GST_BUFFER_PTS(*buffer);
279 break;
280 }
281
282 GstFlowReturn result = pclass->alloc_buffer(pool, buffer, params);
283 g_return_val_if_fail(result == GST_FLOW_OK && *buffer != nullptr, GST_FLOW_ERROR);
284 GstMemory *mem = gst_buffer_peek_memory(*buffer, 0);
285 GstConsumerSurfaceMemory *surfacemem = nullptr;
286 if (gst_is_consumer_surface_memory(mem)) {
287 surfacemem = reinterpret_cast<GstConsumerSurfaceMemory*>(mem);
288 add_buffer_info(surfacepool, surfacemem, *buffer);
289 }
290 priv->available_buf_count--;
291
292 // check whether needs to dropp frame to ensure the maximum frame rate
293 if (surfacemem != nullptr && priv->max_frame_rate > 0 && !priv->is_first_buffer &&
294 drop_this_frame(surfacepool, surfacemem->timestamp, priv->pre_timestamp, priv->max_frame_rate)) {
295 (void)priv->consumer_surface->ReleaseBuffer(surfacemem->surface_buffer, surfacemem->fencefd);
296 if (!priv->flushing && priv->start) {
297 continue;
298 }
299 }
300 cache_frame_if_necessary(surfacepool, surfacemem, *buffer);
301 break;
302 };
303
304 return GST_FLOW_OK;
305 }
306
gst_consumer_surface_pool_init(GstConsumerSurfacePool * pool)307 static void gst_consumer_surface_pool_init(GstConsumerSurfacePool *pool)
308 {
309 g_return_if_fail(pool != nullptr);
310 auto priv = reinterpret_cast<GstConsumerSurfacePoolPrivate *>
311 (gst_consumer_surface_pool_get_instance_private(pool));
312 g_return_if_fail(priv != nullptr);
313 pool->priv = priv;
314 priv->available_buf_count = 0;
315 priv->flushing = FALSE;
316 priv->start = FALSE;
317 priv->suspend = FALSE;
318 priv->is_first_buffer = TRUE;
319 priv->repeat_interval = 0;
320 priv->max_frame_rate = 0;
321 priv->pre_timestamp = 0;
322 priv->cache_buffer = nullptr;
323 g_mutex_init(&priv->pool_lock);
324 g_cond_init(&priv->buffer_available_con);
325 }
326
gst_consumer_surface_pool_buffer_available(GstConsumerSurfacePool * pool)327 static void gst_consumer_surface_pool_buffer_available(GstConsumerSurfacePool *pool)
328 {
329 g_return_if_fail(pool != nullptr && pool->priv != nullptr);
330 auto priv = pool->priv;
331 g_mutex_lock(&priv->pool_lock);
332 ON_SCOPE_EXIT(0) { g_mutex_unlock(&priv->pool_lock); };
333
334 if (priv->suspend) {
335 sptr<SurfaceBuffer> buffer = nullptr;
336 gint32 fencefd = -1;
337 gint64 timestamp = 0;
338 Rect damage = {0, 0, 0, 0};
339 if (priv->consumer_surface->AcquireBuffer(buffer, fencefd, timestamp, damage) == SURFACE_ERROR_OK) {
340 GST_INFO_OBJECT(pool, "Surface is suspended, release buffer");
341 (void)priv->consumer_surface->ReleaseBuffer(buffer, fencefd);
342 return;
343 }
344 }
345
346 if (priv->available_buf_count == 0) {
347 g_cond_signal(&priv->buffer_available_con);
348 }
349 pool->priv->available_buf_count++;
350 GST_DEBUG_OBJECT(pool, "Available buffer count %u", pool->priv->available_buf_count);
351 }
352
gst_consumer_surface_pool_set_surface(GstBufferPool * pool,sptr<Surface> & consumer_surface)353 void gst_consumer_surface_pool_set_surface(GstBufferPool *pool, sptr<Surface> &consumer_surface)
354 {
355 GstConsumerSurfacePool *surfacepool = GST_CONSUMER_SURFACE_POOL(pool);
356 g_return_if_fail(surfacepool != nullptr && surfacepool->priv != nullptr);
357 g_return_if_fail(consumer_surface != nullptr && surfacepool->priv->consumer_surface == nullptr);
358 surfacepool->priv->consumer_surface = consumer_surface;
359 sptr<IBufferConsumerListener> listenerProxy = new (std::nothrow) ConsumerListenerProxy(*surfacepool);
360 g_return_if_fail(listenerProxy != nullptr);
361
362 if (consumer_surface->RegisterConsumerListener(listenerProxy) != SURFACE_ERROR_OK) {
363 GST_WARNING_OBJECT(surfacepool, "register consumer listener fail");
364 }
365 }
366
add_buffer_info(GstConsumerSurfacePool * pool,GstConsumerSurfaceMemory * mem,GstBuffer * buffer)367 static void add_buffer_info(GstConsumerSurfacePool *pool, GstConsumerSurfaceMemory *mem, GstBuffer *buffer)
368 {
369 g_return_if_fail(pool != nullptr && mem != nullptr && buffer != nullptr);
370 uint32_t bufferFlag = 0;
371 if (mem->is_eos_frame) {
372 bufferFlag = BUFFER_FLAG_EOS;
373 }
374 gst_buffer_add_buffer_handle_meta(buffer, mem->buffer_handle, mem->fencefd, bufferFlag);
375
376 if (mem->timestamp < 0) {
377 GST_WARNING_OBJECT(pool, "Invalid timestamp: < 0");
378 GST_BUFFER_PTS(buffer) = 0;
379 } else {
380 GST_BUFFER_PTS(buffer) = static_cast<uint64_t>(mem->timestamp);
381 }
382 }
383
cache_frame_if_necessary(GstConsumerSurfacePool * pool,GstConsumerSurfaceMemory * mem,GstBuffer * buffer)384 static void cache_frame_if_necessary(GstConsumerSurfacePool *pool, GstConsumerSurfaceMemory *mem, GstBuffer *buffer)
385 {
386 g_return_if_fail(pool != nullptr && pool->priv != nullptr && mem != nullptr && buffer != nullptr);
387 auto priv = pool->priv;
388 priv->pre_timestamp = static_cast<uint64_t>(mem->timestamp);
389 if (priv->is_first_buffer) {
390 priv->is_first_buffer = FALSE;
391 } else if (priv->repeat_interval > 0) {
392 if (priv->cache_buffer != nullptr) {
393 gst_buffer_unref(priv->cache_buffer);
394 }
395 priv->cache_buffer = buffer;
396 gst_buffer_ref(priv->cache_buffer);
397 }
398 }
399
drop_this_frame(GstConsumerSurfacePool * pool,guint64 new_timestamp,guint64 old_timestamp,guint32 frame_rate)400 static gboolean drop_this_frame(GstConsumerSurfacePool *pool, guint64 new_timestamp,
401 guint64 old_timestamp, guint32 frame_rate)
402 {
403 if (new_timestamp <= old_timestamp) {
404 GST_WARNING_OBJECT(pool, "Invalid timestamp: not increased");
405 return TRUE;
406 }
407
408 if (frame_rate == 0) {
409 GST_WARNING_OBJECT(pool, "Invalid frame rate: 0");
410 return FALSE;
411 }
412 guint64 min_interval = 1000000000 / frame_rate; // 1s = 1000000000ns
413 if ((UINT64_MAX - min_interval) < old_timestamp) {
414 GST_WARNING_OBJECT(pool, "Invalid timestamp: too big");
415 return TRUE;
416 }
417
418 const guint64 deviations = 3000000; // 3ms
419 if (new_timestamp < (old_timestamp - deviations + min_interval)) {
420 GST_INFO_OBJECT(pool, "Drop this frame to make sure maximum frame rate");
421 return TRUE;
422 }
423 return FALSE;
424 }
425
gst_consumer_surface_pool_new()426 GstBufferPool *gst_consumer_surface_pool_new()
427 {
428 GstBufferPool *pool = GST_BUFFER_POOL_CAST(g_object_new(
429 GST_TYPE_CONSUMER_SURFACE_POOL, "name", "consumer_surfacepool", nullptr));
430 (void)gst_object_ref_sink(pool);
431
432 return pool;
433 }
434