• 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_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