1 /*
2 * Copyright (C) 2022 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_video_capture_pool.h"
17 #include <gst/gst.h>
18 #include "buffer_type_meta.h"
19 #include "surface.h"
20 #include "scope_guard.h"
21 #include "securec.h"
22 #include "media_log.h"
23 #include "media_dfx.h"
24 #include "media_errors.h"
25 using namespace OHOS;
26
27 #define gst_video_capture_pool_parent_class parent_class
28
29 GST_DEBUG_CATEGORY_STATIC(gst_video_capture_pool_debug_category);
30 #define GST_CAT_DEFAULT gst_video_capture_pool_debug_category
31
32 enum {
33 PROP_0,
34 PROP_CACHED_DATA,
35 };
36
37 G_DEFINE_TYPE(GstVideoCapturePool, gst_video_capture_pool, GST_TYPE_CONSUMER_SURFACE_POOL);
38
39 static void gst_video_capture_pool_set_property(GObject *object, guint id, const GValue *value, GParamSpec *pspec);
40 static GstFlowReturn gst_video_capture_pool_buffer_available(GstConsumerSurfacePool *surfacepool, bool *releasebuffer);
41 static GstFlowReturn gst_video_capture_pool_find_buffer(GstBufferPool *gstpool, GstBuffer **buffer, bool *found);
42 static GstFlowReturn gst_video_capture_pool_get_buffer(GstConsumerSurfacePool *surfacepool,
43 GstBuffer **buffer, bool *releasebuffer);
44 static GstFlowReturn gst_video_capture_pool_release_buffer(GstConsumerSurfacePool *surfacepool, bool *releasebuffer);
45
gst_video_capture_pool_class_init(GstVideoCapturePoolClass * klass)46 static void gst_video_capture_pool_class_init(GstVideoCapturePoolClass *klass)
47 {
48 g_return_if_fail(klass != nullptr);
49 GObjectClass *gobjectClass = G_OBJECT_CLASS(klass);
50 GST_DEBUG_CATEGORY_INIT(gst_video_capture_pool_debug_category, "videocapturepool", 0,
51 "video capture pool base class");
52
53 gobjectClass->set_property = gst_video_capture_pool_set_property;
54
55 g_object_class_install_property(gobjectClass, PROP_CACHED_DATA,
56 g_param_spec_boolean("cached-data", "es pause", "es pause",
57 FALSE, (GParamFlags)(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)));
58 }
59
gst_video_capture_pool_init(GstVideoCapturePool * pool)60 static void gst_video_capture_pool_init(GstVideoCapturePool *pool)
61 {
62 g_return_if_fail(pool != nullptr);
63 GstConsumerSurfacePool *surfacepool = GST_CONSUMER_SURFACE_POOL(pool);
64 surfacepool->buffer_available = gst_video_capture_pool_buffer_available;
65 surfacepool->find_buffer = gst_video_capture_pool_find_buffer;
66
67 pool->cached_data = false;
68 pool->poolMgr = nullptr;
69 }
70
gst_video_capture_pool_new()71 GstBufferPool *gst_video_capture_pool_new()
72 {
73 GstBufferPool *pool = GST_BUFFER_POOL_CAST(g_object_new(
74 GST_TYPE_VIDEO_CAPTURE_POOL, "name", "video_capture_pool", nullptr));
75 (void)gst_object_ref_sink(pool);
76
77 return pool;
78 }
79
gst_video_capture_pool_set_property(GObject * object,guint id,const GValue * value,GParamSpec * pspec)80 static void gst_video_capture_pool_set_property(GObject *object, guint id, const GValue *value, GParamSpec *pspec)
81 {
82 (void)pspec;
83 GstVideoCapturePool *pool = GST_VIDEO_CAPTURE_POOL(object);
84 g_return_if_fail(pool != nullptr && value != nullptr);
85
86 g_mutex_lock(&pool->pool_lock);
87 ON_SCOPE_EXIT(0) {
88 g_mutex_unlock(&pool->pool_lock);
89 };
90 switch (id) {
91 case PROP_CACHED_DATA:
92 if (g_value_get_boolean(value) == true) {
93 if (pool->poolMgr == nullptr) {
94 const uint32_t size = 6; // Save up to 6 buffer data.
95 pool->poolMgr = std::make_shared<OHOS::Media::VideoPoolManager>(size);
96 g_return_if_fail(pool->poolMgr != nullptr);
97 }
98
99 // If the queue is not empty, it is not supported to cache the suspended data again.
100 if (pool->poolMgr->IsBufferQueEmpty()) {
101 pool->cached_data = true;
102 }
103 } else {
104 pool->cached_data = false;
105 if (pool->poolMgr != nullptr) {
106 GST_DEBUG_OBJECT(pool, "Received %d frames of data during pause.",
107 pool->poolMgr->GetBufferQueSize());
108 }
109 }
110 break;
111 default:
112 break;
113 }
114 }
115
gst_video_capture_pool_buffer_available(GstConsumerSurfacePool * surfacepool,bool * releasebuffer)116 static GstFlowReturn gst_video_capture_pool_buffer_available(GstConsumerSurfacePool *surfacepool, bool *releasebuffer)
117 {
118 g_return_val_if_fail(surfacepool != nullptr && releasebuffer != nullptr, GST_FLOW_ERROR);
119 GstVideoCapturePool *pool = GST_VIDEO_CAPTURE_POOL(surfacepool);
120
121 g_mutex_lock(&pool->pool_lock);
122 ON_SCOPE_EXIT(0) {
123 g_mutex_unlock(&pool->pool_lock);
124 };
125
126 *releasebuffer = false;
127 if (pool->cached_data) {
128 if (pool->poolMgr != nullptr && pool->poolMgr->IsBufferQueFull() == false) {
129 GstBuffer *buf;
130 if (gst_video_capture_pool_get_buffer(surfacepool, &buf, releasebuffer) == GST_FLOW_OK) {
131 (void)pool->poolMgr->PushBuffer(buf);
132 *releasebuffer = false;
133 } else {
134 GST_WARNING_OBJECT(surfacepool, "video capture pool get buffer failed");
135 }
136 }
137 } else {
138 if (gst_video_capture_pool_release_buffer(surfacepool, releasebuffer) != GST_FLOW_OK) {
139 GST_WARNING_OBJECT(surfacepool, "video capture pool release buffer failed");
140 }
141 }
142
143 return GST_FLOW_OK;
144 }
145
gst_video_capture_pool_find_buffer(GstBufferPool * gstpool,GstBuffer ** buffer,bool * found)146 static GstFlowReturn gst_video_capture_pool_find_buffer(GstBufferPool *gstpool, GstBuffer **buffer, bool *found)
147 {
148 GstVideoCapturePool *pool = GST_VIDEO_CAPTURE_POOL(gstpool);
149 g_return_val_if_fail(pool != nullptr && buffer != nullptr && found != nullptr, GST_FLOW_ERROR);
150
151 g_mutex_lock(&pool->pool_lock);
152 ON_SCOPE_EXIT(0) {
153 g_mutex_unlock(&pool->pool_lock);
154 };
155
156 *found = false;
157 if (pool->poolMgr != nullptr && pool->poolMgr->GetBufferQueSize() > 0) {
158 *buffer = pool->poolMgr->PopBuffer();
159 *found = true;
160 }
161
162 return GST_FLOW_OK;
163 }
164
gst_video_capture_pool_get_buffer(GstConsumerSurfacePool * surfacepool,GstBuffer ** buffer,bool * releasebuffer)165 static GstFlowReturn gst_video_capture_pool_get_buffer(GstConsumerSurfacePool *surfacepool,
166 GstBuffer **buffer, bool *releasebuffer)
167 {
168 g_return_val_if_fail(surfacepool != nullptr && buffer != nullptr && surfacepool->get_surface_buffer != nullptr &&
169 surfacepool->release_surface_buffer != nullptr && releasebuffer != nullptr, GST_FLOW_ERROR);
170
171 // Get buffer
172 OHOS::sptr<OHOS::SurfaceBuffer> surfacebuffer = nullptr;
173 gint32 fencefd = -1;
174 GstFlowReturn ret = surfacepool->get_surface_buffer(surfacepool, surfacebuffer, fencefd);
175 g_return_val_if_fail(ret == GST_FLOW_OK && surfacebuffer != nullptr, GST_FLOW_ERROR);
176 *releasebuffer = true;
177 ON_SCOPE_EXIT(0) {
178 surfacepool->release_surface_buffer(surfacepool, surfacebuffer, fencefd);
179 };
180
181 // Get buffer data.
182 gint64 timestamp = 0;
183 gint32 data_size = 0;
184 gboolean end_of_stream = false;
185 const OHOS::sptr<OHOS::BufferExtraData>& extraData = surfacebuffer->GetExtraData();
186 g_return_val_if_fail(extraData != nullptr, GST_FLOW_ERROR);
187 (void)extraData->ExtraGet("timeStamp", timestamp);
188 (void)extraData->ExtraGet("dataSize", data_size);
189 g_return_val_if_fail(static_cast<gint32>(surfacebuffer->GetSize()) >= data_size, GST_FLOW_ERROR);
190 (void)extraData->ExtraGet("endOfStream", end_of_stream);
191
192 // copy data
193 GstBuffer *dts_buffer = gst_buffer_new_allocate(nullptr, data_size, nullptr);
194 g_return_val_if_fail(dts_buffer != nullptr, GST_FLOW_ERROR);
195 ON_SCOPE_EXIT(1) {
196 gst_buffer_unref(dts_buffer);
197 };
198
199 GstMapInfo info = GST_MAP_INFO_INIT;
200 g_return_val_if_fail(gst_buffer_map(dts_buffer, &info, GST_MAP_WRITE) == TRUE, GST_FLOW_ERROR);
201 ON_SCOPE_EXIT(2) { // ON_SCOPE_EXIT 2
202 gst_buffer_unmap(dts_buffer, &info);
203 };
204
205 uint8_t *src = static_cast<uint8_t *>(surfacebuffer->GetVirAddr());
206 errno_t rc = memcpy_s(info.data, info.size, src, static_cast<size_t>(data_size));
207 g_return_val_if_fail(rc == EOK, GST_FLOW_ERROR);
208
209 // Meta
210 GstBufferTypeMeta *meta = (GstBufferTypeMeta *)gst_buffer_add_meta(dts_buffer, GST_BUFFER_TYPE_META_INFO, NULL);
211 g_return_val_if_fail(meta != NULL, GST_FLOW_ERROR);
212
213 meta->type = BUFFER_TYPE_HANDLE;
214 meta->bufLen = static_cast<uint32_t>(data_size);
215 meta->length = static_cast<uint32_t>(data_size);
216 meta->bufferFlag = end_of_stream ? BUFFER_FLAG_EOS : 0;
217 meta->pixelFormat = surfacebuffer->GetFormat();
218 meta->width = static_cast<uint32_t>(surfacebuffer->GetWidth());
219 meta->height = static_cast<uint32_t>(surfacebuffer->GetHeight());
220 meta->invalidpts = TRUE;
221
222 GST_BUFFER_PTS(dts_buffer) = static_cast<uint64_t>(timestamp);
223 GST_DEBUG_OBJECT(surfacepool, "BufferQue video capture buffer size is: %" G_GSIZE_FORMAT ", pts: %"
224 G_GUINT64_FORMAT, gst_buffer_get_size(dts_buffer), timestamp);
225
226 CANCEL_SCOPE_EXIT_GUARD(1);
227 *buffer = dts_buffer;
228 return GST_FLOW_OK;
229 }
230
gst_video_capture_pool_release_buffer(GstConsumerSurfacePool * surfacepool,bool * releasebuffer)231 static GstFlowReturn gst_video_capture_pool_release_buffer(GstConsumerSurfacePool *surfacepool, bool *releasebuffer)
232 {
233 g_return_val_if_fail(surfacepool != nullptr && surfacepool->get_surface_buffer != nullptr &&
234 surfacepool->release_surface_buffer != nullptr && releasebuffer != nullptr, GST_FLOW_ERROR);
235
236 // Get buffer
237 OHOS::sptr<OHOS::SurfaceBuffer> surfacebuffer = nullptr;
238 gint32 fencefd = -1;
239 GstFlowReturn ret = surfacepool->get_surface_buffer(surfacepool, surfacebuffer, fencefd);
240 g_return_val_if_fail(ret == GST_FLOW_OK && surfacebuffer != nullptr, GST_FLOW_ERROR);
241 *releasebuffer = true;
242 surfacepool->release_surface_buffer(surfacepool, surfacebuffer, fencefd);
243
244 return GST_FLOW_OK;
245 }