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_surface_mem_sink.h"
17 #include <cinttypes>
18 #include "surface.h"
19 #include "gst_producer_surface_pool.h"
20 #include "buffer_type_meta.h"
21 #include "media_log.h"
22 #include "param_wrapper.h"
23 #include "scope_guard.h"
24 #include "media_dfx.h"
25 #include "av_common.h"
26
27 using namespace OHOS;
28 using namespace OHOS::Media;
29
30 struct _GstSurfaceMemSinkPrivate {
31 OHOS::sptr<OHOS::Surface> surface;
32 GstProducerSurfacePool *pool;
33 guint rotation;
34 };
35
36 enum {
37 PROP_0,
38 PROP_SURFACE,
39 PROP_SURFACE_POOL,
40 PROP_CACHE_BUFFERS_NUM,
41 PROP_PERFORMANCE_MODE,
42 PROP_VIDEO_SCALE_TYPE,
43 PROP_VIDEO_ROTATION,
44 };
45
46 static GstStaticPadTemplate g_sinktemplate = GST_STATIC_PAD_TEMPLATE("sink",
47 GST_PAD_SINK,
48 GST_PAD_ALWAYS,
49 GST_STATIC_CAPS_ANY);
50
51 static void gst_surface_mem_sink_dispose(GObject *obj);
52 static void gst_surface_mem_sink_finalize(GObject *obj);
53 static void gst_surface_mem_sink_set_property(GObject *object, guint propId, const GValue *value, GParamSpec *pspec);
54 static void gst_surface_mem_sink_get_property(GObject *object, guint propId, GValue *value, GParamSpec *pspec);
55 static gboolean gst_surface_mem_sink_do_propose_allocation(GstMemSink *memsink, GstQuery *query);
56 static GstFlowReturn gst_surface_mem_sink_do_app_render(GstMemSink *memsink, GstBuffer *buffer, bool is_preroll);
57 static void gst_surface_mem_sink_dump_from_sys_param(GstSurfaceMemSink *self);
58 static void gst_surface_mem_sink_dump_buffer(GstSurfaceMemSink *self, GstBuffer *buffer);
59 static GstStateChangeReturn gst_surface_mem_sink_change_state(GstElement *element, GstStateChange transition);
60 static gboolean gst_surface_mem_sink_event(GstBaseSink *bsink, GstEvent *event);
61 static GstFlowReturn gst_surface_mem_sink_subclass_do_app_render(GstSurfaceMemSink *sink,
62 GstBuffer *buffer, bool is_preroll);
63
64 #define gst_surface_mem_sink_parent_class parent_class
65 G_DEFINE_TYPE_WITH_CODE(GstSurfaceMemSink, gst_surface_mem_sink,
66 GST_TYPE_MEM_SINK, G_ADD_PRIVATE(GstSurfaceMemSink));
67
68 GST_DEBUG_CATEGORY_STATIC(gst_surface_mem_sink_debug_category);
69 #define GST_CAT_DEFAULT gst_surface_mem_sink_debug_category
70
gst_surface_mem_sink_class_init(GstSurfaceMemSinkClass * klass)71 static void gst_surface_mem_sink_class_init(GstSurfaceMemSinkClass *klass)
72 {
73 g_return_if_fail(klass != nullptr);
74
75 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
76 GstMemSinkClass *mem_sink_class = GST_MEM_SINK_CLASS(klass);
77 GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
78 GstBaseSinkClass *base_sink_class = GST_BASE_SINK_CLASS(klass);
79
80 gst_element_class_add_static_pad_template(element_class, &g_sinktemplate);
81
82 gobject_class->dispose = gst_surface_mem_sink_dispose;
83 gobject_class->finalize = gst_surface_mem_sink_finalize;
84 gobject_class->set_property = gst_surface_mem_sink_set_property;
85 gobject_class->get_property = gst_surface_mem_sink_get_property;
86 element_class->change_state = gst_surface_mem_sink_change_state;
87
88 gst_element_class_set_static_metadata(element_class,
89 "SurfaceMemSink", "Sink/Video",
90 "Output to surface buffer and allow the application to get access to the surface buffer",
91 "OpenHarmony");
92
93 g_object_class_install_property(gobject_class, PROP_SURFACE,
94 g_param_spec_pointer("surface", "Surface",
95 "Surface for rendering output",
96 (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
97
98 g_object_class_install_property(gobject_class, PROP_SURFACE_POOL,
99 g_param_spec_pointer("surface-pool", "Surface Pool",
100 "Surface pool for rendering output buffers",
101 (GParamFlags)(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
102
103 g_object_class_install_property(gobject_class, PROP_CACHE_BUFFERS_NUM,
104 g_param_spec_uint("cache-buffers-num", "Cache Buffers Num",
105 "The cache buffers num in pool",
106 0, G_MAXUINT, 0, (GParamFlags)(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)));
107
108 g_object_class_install_property(gobject_class, PROP_PERFORMANCE_MODE,
109 g_param_spec_boolean("performance-mode", "performance mode", "performance mode",
110 FALSE, (GParamFlags)(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)));
111
112 g_object_class_install_property(gobject_class, PROP_VIDEO_SCALE_TYPE,
113 g_param_spec_uint("video-scale-type", "Video Scale Type",
114 "Set video scale type for graphic",
115 0, G_MAXUINT, 0, (GParamFlags)(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)));
116
117 g_object_class_install_property(gobject_class, PROP_VIDEO_ROTATION,
118 g_param_spec_uint("video-rotation", "Video Rotation",
119 "Set video rotation for surface",
120 0, G_MAXUINT, 0, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
121
122 mem_sink_class->do_propose_allocation = gst_surface_mem_sink_do_propose_allocation;
123 mem_sink_class->do_app_render = gst_surface_mem_sink_do_app_render;
124 base_sink_class->event = gst_surface_mem_sink_event;
125 klass->do_app_render = gst_surface_mem_sink_subclass_do_app_render;
126 GST_DEBUG_CATEGORY_INIT(gst_surface_mem_sink_debug_category, "surfacesink", 0, "surfacesink class");
127 }
128
gst_surface_mem_sink_init(GstSurfaceMemSink * sink)129 static void gst_surface_mem_sink_init(GstSurfaceMemSink *sink)
130 {
131 g_return_if_fail(sink != nullptr);
132 GST_DEBUG_OBJECT(sink, "Init, id = %d", static_cast<int32_t>(FAKE_POINTER(sink)));
133
134 auto priv = reinterpret_cast<GstSurfaceMemSinkPrivate *>(gst_surface_mem_sink_get_instance_private(sink));
135 g_return_if_fail(priv != nullptr);
136 sink->priv = priv;
137 sink->priv->surface = nullptr;
138 sink->priv->pool = GST_PRODUCER_SURFACE_POOL_CAST(gst_producer_surface_pool_new());
139 sink->priv->rotation = 0;
140 sink->prerollBuffer = nullptr;
141 sink->firstRenderFrame = TRUE;
142 sink->preInitPool = FALSE;
143 sink->dump.enable_dump = FALSE;
144 sink->dump.dump_file = nullptr;
145 sink->performanceMode = FALSE;
146 sink->lastRate = 0;
147 sink->renderCnt = 0;
148 GstMemSink *memSink = GST_MEM_SINK_CAST(sink);
149 memSink->max_pool_capacity = SURFACE_MAX_QUEUE_SIZE;
150 gst_surface_mem_sink_dump_from_sys_param(sink);
151 }
152
gst_surface_mem_sink_dispose(GObject * obj)153 static void gst_surface_mem_sink_dispose(GObject *obj)
154 {
155 g_return_if_fail(obj != nullptr);
156
157 GstSurfaceMemSink *surface_sink = GST_SURFACE_MEM_SINK_CAST(obj);
158 GstSurfaceMemSinkPrivate *priv = surface_sink->priv;
159 g_return_if_fail(priv != nullptr);
160
161 GST_OBJECT_LOCK(surface_sink);
162 priv->surface = nullptr;
163 if (priv->pool != nullptr) {
164 gst_object_unref(priv->pool);
165 priv->pool = nullptr;
166 }
167 GST_OBJECT_UNLOCK(surface_sink);
168
169 G_OBJECT_CLASS(parent_class)->dispose(obj);
170 }
171
gst_surface_mem_sink_finalize(GObject * obj)172 static void gst_surface_mem_sink_finalize(GObject *obj)
173 {
174 g_return_if_fail(obj != nullptr);
175 GstSurfaceMemSink *surface_sink = GST_SURFACE_MEM_SINK_CAST(obj);
176 gst_caps_unref(surface_sink->caps);
177 G_OBJECT_CLASS(parent_class)->finalize(obj);
178 }
179
gst_surface_mem_sink_get_rotation(guint rotation)180 static GraphicTransformType gst_surface_mem_sink_get_rotation(guint rotation)
181 {
182 // We gets the rotation direction clockwise, but Surface needs it counterclockwise
183 switch (rotation) {
184 case VIDEO_ROTATION_90: {
185 return GRAPHIC_ROTATE_270;
186 }
187 case VIDEO_ROTATION_180: {
188 return GRAPHIC_ROTATE_180;
189 }
190 case VIDEO_ROTATION_270: {
191 return GRAPHIC_ROTATE_90;
192 }
193 default:
194 return GRAPHIC_ROTATE_NONE;
195 }
196 }
197
gst_surface_mem_sink_set_property(GObject * object,guint propId,const GValue * value,GParamSpec * pspec)198 static void gst_surface_mem_sink_set_property(GObject *object, guint propId, const GValue *value, GParamSpec *pspec)
199 {
200 g_return_if_fail(object != nullptr && value != nullptr);
201
202 GstSurfaceMemSink *surface_sink = GST_SURFACE_MEM_SINK_CAST(object);
203 GstSurfaceMemSinkPrivate *priv = surface_sink->priv;
204 g_return_if_fail(priv != nullptr);
205
206 switch (propId) {
207 case PROP_SURFACE: {
208 gpointer surface = g_value_get_pointer(value);
209 g_return_if_fail(surface != nullptr);
210 OHOS::sptr<OHOS::Surface> surface_ref = reinterpret_cast<OHOS::Surface *>(surface);
211 GST_OBJECT_LOCK(surface_sink);
212 priv->surface = surface_ref;
213 gst_producer_surface_pool_set_surface(priv->pool, surface_ref);
214 GST_OBJECT_UNLOCK(surface_sink);
215 break;
216 }
217 case PROP_CACHE_BUFFERS_NUM: {
218 guint cache_buffer_num = g_value_get_uint(value);
219 g_object_set(G_OBJECT(priv->pool), "cache-buffers-num", cache_buffer_num, nullptr);
220 break;
221 }
222 case PROP_PERFORMANCE_MODE:
223 surface_sink->performanceMode = g_value_get_boolean(value);
224 break;
225
226 case PROP_VIDEO_SCALE_TYPE: {
227 guint video_scale_type = g_value_get_uint(value);
228 g_object_set(G_OBJECT(priv->pool), "video-scale-type", video_scale_type, nullptr);
229 break;
230 }
231 case PROP_VIDEO_ROTATION: {
232 priv->rotation = g_value_get_uint(value);
233 break;
234 }
235 default:
236 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
237 break;
238 }
239 }
240
gst_surface_mem_sink_get_property(GObject * object,guint propId,GValue * value,GParamSpec * pspec)241 static void gst_surface_mem_sink_get_property(GObject *object, guint propId, GValue *value, GParamSpec *pspec)
242 {
243 g_return_if_fail(object != nullptr);
244
245 GstSurfaceMemSink *surface_sink = GST_SURFACE_MEM_SINK_CAST(object);
246 GstSurfaceMemSinkPrivate *priv = surface_sink->priv;
247 g_return_if_fail(priv != nullptr);
248
249 switch (propId) {
250 case PROP_SURFACE: {
251 GST_OBJECT_LOCK(surface_sink);
252 g_return_if_fail(priv->surface != nullptr);
253 g_value_set_pointer(value, priv->surface.GetRefPtr());
254 GST_OBJECT_UNLOCK(surface_sink);
255 break;
256 }
257 case PROP_SURFACE_POOL: {
258 GST_OBJECT_LOCK(surface_sink);
259 ON_SCOPE_EXIT(0) { GST_OBJECT_UNLOCK(surface_sink); };
260 g_value_set_pointer(value, priv->pool);
261 GstSurfaceAllocator *allocator = gst_surface_allocator_new();
262 g_return_if_fail(allocator != nullptr);
263 ON_SCOPE_EXIT(1) { gst_object_unref(allocator); };
264 GstStructure *config = gst_buffer_pool_get_config(GST_BUFFER_POOL(priv->pool));
265 g_return_if_fail(config != nullptr);
266 gst_buffer_pool_config_set_allocator(config, GST_ALLOCATOR_CAST(allocator), nullptr);
267 (void)gst_buffer_pool_set_config(GST_BUFFER_POOL(priv->pool), config);
268 surface_sink->preInitPool = TRUE;
269 break;
270 }
271 case PROP_VIDEO_ROTATION: {
272 GST_OBJECT_LOCK(surface_sink);
273 g_value_set_uint(value, priv->rotation);
274 GST_OBJECT_UNLOCK(surface_sink);
275 break;
276 }
277 default:
278 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
279 break;
280 }
281 }
282
gst_surface_mem_sink_drop_frame_check(GstSurfaceMemSink * surface_sink)283 static gboolean gst_surface_mem_sink_drop_frame_check(GstSurfaceMemSink *surface_sink)
284 {
285 GstBaseSink *gst_base_sink = GST_BASE_SINK(surface_sink);
286 static guint dropFrameArray[] = {0, 1, 0, 1, 0, 0, 1};
287
288 guint rate = (guint)(gst_base_sink->segment.rate * 100); // 100 : rate double convert to uint
289 if (surface_sink->lastRate != rate) {
290 surface_sink->lastRate = rate;
291 surface_sink->renderCnt = 0;
292 }
293 surface_sink->renderCnt++;
294
295 if ((rate == 125 && surface_sink->renderCnt % 5 == 0) || // 125 : 1.25 * 100 && 5 : drop frame inteval
296 (rate == 175 && dropFrameArray[surface_sink->renderCnt % 7]) || // 175 : 1.75 * 100 && 7 : drop frame inteval
297 (rate == 200 && surface_sink->renderCnt % 2 == 0)) { // 200 : 2.00 * 100 && 2 : drop frame inteval
298 return FALSE;
299 }
300
301 return TRUE;
302 }
303
gst_surface_mem_sink_subclass_do_app_render(GstSurfaceMemSink * sink,GstBuffer * buffer,bool is_preroll)304 static GstFlowReturn gst_surface_mem_sink_subclass_do_app_render(GstSurfaceMemSink *sink,
305 GstBuffer *buffer, bool is_preroll)
306 {
307 (void)sink;
308 (void)buffer;
309 (void)is_preroll;
310
311 return GST_FLOW_OK;
312 }
313
gst_surface_mem_sink_need_flush(GstSurfaceMemSink * surface_sink,GstBuffer * buffer,bool is_preroll)314 static gboolean gst_surface_mem_sink_need_flush(GstSurfaceMemSink *surface_sink, GstBuffer *buffer, bool is_preroll)
315 {
316 gboolean needFlush = TRUE;
317 if (is_preroll) {
318 surface_sink->prerollBuffer = buffer;
319 } else {
320 if (surface_sink->prerollBuffer == buffer) {
321 // if it's paused, then play, this buffer is render by preroll
322 surface_sink->prerollBuffer = nullptr;
323 needFlush = FALSE;
324 }
325 }
326 return needFlush;
327 }
328
gst_surface_do_render_buffer(GstMemSink * memsink,GstBuffer * buffer,bool is_preroll)329 static GstFlowReturn gst_surface_do_render_buffer(GstMemSink *memsink, GstBuffer *buffer, bool is_preroll)
330 {
331 g_return_val_if_fail(memsink != nullptr && buffer != nullptr, GST_FLOW_ERROR);
332 GstSurfaceMemSink *surface_sink = GST_SURFACE_MEM_SINK_CAST(memsink);
333 g_return_val_if_fail(surface_sink != nullptr, GST_FLOW_ERROR);
334 GstSurfaceMemSinkPrivate *priv = surface_sink->priv;
335 g_return_val_if_fail(priv != nullptr, GST_FLOW_ERROR);
336
337 for (guint i = 0; i < gst_buffer_n_memory(buffer); i++) {
338 GstMemory *memory = gst_buffer_peek_memory(buffer, i);
339 if (!gst_is_surface_memory(memory)) {
340 GST_WARNING_OBJECT(surface_sink, "not surface buffer !, 0x%06" PRIXPTR, FAKE_POINTER(memory));
341 continue;
342 }
343
344 GstSurfaceMemory *surface_mem = reinterpret_cast<GstSurfaceMemory *>(memory);
345
346 gboolean needFlush = gst_surface_mem_sink_need_flush(surface_sink, buffer, is_preroll);
347 if (needFlush) {
348 // surface do not support timestamp is 0.
349 GST_BUFFER_PTS(buffer) = GST_BUFFER_PTS(buffer) == 0 ? 1 : GST_BUFFER_PTS(buffer);
350 OHOS::BufferFlushConfig flushConfig = {
351 { 0, 0, surface_mem->buf->GetWidth(), surface_mem->buf->GetHeight() }, GST_BUFFER_PTS(buffer)
352 };
353 gst_surface_mem_sink_dump_buffer(surface_sink, buffer);
354 {
355 MediaTrace trace("Surface::FlushBuffer");
356 surface_mem->need_render = TRUE;
357 OHOS::SurfaceError ret = priv->surface->FlushBuffer(surface_mem->buf, surface_mem->fence, flushConfig);
358 if (ret != OHOS::SurfaceError::SURFACE_ERROR_OK) {
359 surface_mem->need_render = FALSE;
360 GST_ERROR_OBJECT(surface_sink, "flush buffer to surface failed, %d", ret);
361 }
362 }
363 }
364 }
365 return GST_FLOW_OK;
366 }
367
gst_surface_mem_sink_do_app_render(GstMemSink * memsink,GstBuffer * buffer,bool is_preroll)368 static GstFlowReturn gst_surface_mem_sink_do_app_render(GstMemSink *memsink, GstBuffer *buffer, bool is_preroll)
369 {
370 g_return_val_if_fail(memsink != nullptr && buffer != nullptr, GST_FLOW_ERROR);
371 GstSurfaceMemSink *surface_sink = GST_SURFACE_MEM_SINK_CAST(memsink);
372 g_return_val_if_fail(surface_sink != nullptr, GST_FLOW_ERROR);
373
374 GstSurfaceMemSinkClass *surface_sink_class = GST_SURFACE_MEM_SINK_GET_CLASS(surface_sink);
375 GST_OBJECT_LOCK(surface_sink);
376
377 if (gst_surface_mem_sink_drop_frame_check(surface_sink) == FALSE) {
378 GST_OBJECT_UNLOCK(surface_sink);
379 GST_DEBUG_OBJECT(surface_sink, "user set rate, drop same frame");
380 return GST_FLOW_OK;
381 }
382
383 /* Make sure set rotation information for each video, avoid the rotation
384 * information of the previous video affecting the subsequent video
385 */
386 if (surface_sink->firstRenderFrame) {
387 GST_DEBUG_OBJECT(surface_sink, "set rotation: %u", surface_sink->priv->rotation);
388 if (surface_sink->priv->surface) {
389 MediaTrace trace("Surface::SetTransform");
390 (void)surface_sink->priv->surface->SetTransform(
391 gst_surface_mem_sink_get_rotation(surface_sink->priv->rotation));
392 }
393 }
394
395 if (surface_sink->firstRenderFrame && is_preroll) {
396 GST_DEBUG_OBJECT(surface_sink, "first render frame");
397 surface_sink->firstRenderFrame = FALSE;
398 GST_OBJECT_UNLOCK(surface_sink);
399 return GST_FLOW_OK;
400 }
401
402 (void)gst_surface_do_render_buffer(memsink, buffer, is_preroll);
403
404 GST_OBJECT_UNLOCK(surface_sink);
405
406 if (surface_sink_class->do_app_render != nullptr) {
407 (void)surface_sink_class->do_app_render(surface_sink, buffer, is_preroll);
408 }
409 GST_DEBUG_OBJECT(surface_sink, "End gst_surface_mem_sink_do_app_render");
410 return GST_FLOW_OK;
411 }
412
gst_surface_mem_sink_do_propose_allocation(GstMemSink * memsink,GstQuery * query)413 static gboolean gst_surface_mem_sink_do_propose_allocation(GstMemSink *memsink, GstQuery *query)
414 {
415 g_return_val_if_fail(memsink != nullptr && query != nullptr, FALSE);
416 GstSurfaceMemSink *surface_sink = GST_SURFACE_MEM_SINK_CAST(memsink);
417 g_return_val_if_fail(surface_sink != nullptr, FALSE);
418
419 GstCaps *caps = nullptr;
420 gboolean needPool = FALSE;
421 gst_query_parse_allocation(query, &caps, &needPool);
422
423 if (!needPool) {
424 GST_ERROR_OBJECT(surface_sink, "no need buffer pool, unexpected!");
425 return FALSE;
426 }
427 if (surface_sink->performanceMode && surface_sink->preInitPool) {
428 GST_INFO_OBJECT(surface_sink, "pool pre init");
429 surface_sink->preInitPool = FALSE;
430 return TRUE;
431 }
432
433 GST_OBJECT_LOCK(surface_sink);
434 ON_SCOPE_EXIT(0) { GST_OBJECT_UNLOCK(surface_sink); };
435
436 guint size = 0;
437 guint minBuffers = 0;
438 guint maxBuffers = 0;
439 gst_query_parse_nth_allocation_pool(query, 0, nullptr, &size, &minBuffers, &maxBuffers);
440 if (maxBuffers == 0) {
441 GST_INFO_OBJECT(surface_sink, "correct the maxbuffer from %u to %u", maxBuffers, memsink->max_pool_capacity);
442 maxBuffers = memsink->max_pool_capacity;
443 }
444 GST_DEBUG("maxBuffers is: %u", maxBuffers);
445
446 GstProducerSurfacePool *pool = surface_sink->priv->pool;
447 g_return_val_if_fail(pool != nullptr, FALSE);
448 g_return_val_if_fail(gst_buffer_pool_set_active(GST_BUFFER_POOL(pool), FALSE), FALSE);
449
450 GstVideoInfo info;
451 GST_DEBUG("begin gst_video_info_from_caps");
452 gboolean ret = gst_video_info_from_caps(&info, caps);
453 g_return_val_if_fail(ret, FALSE);
454 gst_query_add_allocation_pool(query, GST_BUFFER_POOL_CAST(pool), info.size, minBuffers, maxBuffers);
455
456 GstSurfaceAllocator *allocator = gst_surface_allocator_new();
457 g_return_val_if_fail(allocator != nullptr, FALSE);
458 ON_SCOPE_EXIT(1) { gst_object_unref(allocator); };
459 GstStructure *params = gst_structure_new("mem", "memtype", G_TYPE_STRING, "surface", nullptr);
460 gst_query_add_allocation_param(query, GST_ALLOCATOR_CAST(allocator), nullptr);
461 gst_query_add_allocation_meta(query, GST_VIDEO_META_API_TYPE, nullptr);
462 gst_query_add_allocation_meta(query, GST_BUFFER_TYPE_META_API_TYPE, params);
463 gst_structure_free(params);
464
465 GstStructure *config = gst_buffer_pool_get_config(GST_BUFFER_POOL_CAST(pool));
466 g_return_val_if_fail(config != nullptr, FALSE);
467
468 gst_buffer_pool_config_set_params(config, caps, info.size, minBuffers, maxBuffers);
469 gst_buffer_pool_config_set_allocator(config, GST_ALLOCATOR_CAST(allocator), nullptr);
470 // set config will take ownership of the config, we dont need to free it.
471 ret = gst_buffer_pool_set_config(GST_BUFFER_POOL_CAST(pool), config);
472
473 return ret;
474 }
475
gst_surface_mem_sink_dump_from_sys_param(GstSurfaceMemSink * self)476 void gst_surface_mem_sink_dump_from_sys_param(GstSurfaceMemSink *self)
477 {
478 std::string dump_enable;
479 self->dump.enable_dump = FALSE;
480 int32_t res = OHOS::system::GetStringParameter("sys.media.dump.frame.enable", dump_enable, "");
481 if (res != 0 || dump_enable.empty()) {
482 GST_DEBUG_OBJECT(self, "sys.media.dump.frame.enable");
483 return;
484 }
485 GST_DEBUG_OBJECT(self, "sys.media.dump.frame.enable=%s", dump_enable.c_str());
486
487 if (dump_enable == "true") {
488 self->dump.enable_dump = TRUE;
489 }
490 }
491
gst_surface_mem_sink_event(GstBaseSink * bsink,GstEvent * event)492 static gboolean gst_surface_mem_sink_event(GstBaseSink *bsink, GstEvent *event)
493 {
494 GstSurfaceMemSink *surface_mem_sink = GST_SURFACE_MEM_SINK(bsink);
495 g_return_val_if_fail(surface_mem_sink != nullptr, FALSE);
496 g_return_val_if_fail(event != nullptr, FALSE);
497
498 GST_DEBUG_OBJECT(surface_mem_sink, "event->type %d", event->type);
499 switch (event->type) {
500 case GST_EVENT_CAPS: {
501 GstCaps *caps;
502 gst_event_parse_caps(event, &caps);
503 surface_mem_sink->caps = caps;
504 gst_caps_ref(surface_mem_sink->caps);
505 break;
506 }
507 default:
508 break;
509 }
510 return GST_BASE_SINK_CLASS(parent_class)->event(bsink, event);
511 }
512
gst_surface_mem_sink_dump_buffer(GstSurfaceMemSink * self,GstBuffer * buffer)513 static void gst_surface_mem_sink_dump_buffer(GstSurfaceMemSink *self, GstBuffer *buffer)
514 {
515 g_return_if_fail(self != nullptr);
516 g_return_if_fail(buffer != nullptr);
517 if (self->dump.enable_dump == FALSE) {
518 return;
519 }
520 GST_DEBUG_OBJECT(self, "Dump yuv buffer");
521
522 if (self->dump.dump_file == nullptr) {
523 GST_ERROR_OBJECT(self, "file not opened");
524 return;
525 }
526
527 GstVideoMeta *video_meta = gst_buffer_get_video_meta(buffer);
528 g_return_if_fail(video_meta != nullptr);
529 GstStructure *struc = gst_caps_get_structure(self->caps, 0);
530 g_return_if_fail(struc != nullptr);
531 const gchar *format = gst_structure_get_string(struc, "format");
532 g_return_if_fail(format != nullptr);
533
534 GstMapInfo info = GST_MAP_INFO_INIT;
535 g_return_if_fail(gst_buffer_map(buffer, &info, GST_MAP_READ));
536
537 guint stride_width = 0;
538 guint stride_height = 0;
539 guint stride_size = info.size;
540 if (g_str_equal(format, "NV12") || g_str_equal(format, "NV21")) {
541 stride_width = video_meta->stride[0];
542 if (stride_width != 0) {
543 stride_height = video_meta->offset[1] / stride_width;
544 if (stride_height % 32 != 0) { // 32 : 高对齐
545 stride_height = ((stride_height / 32) + 1) * 32; // 32 : 高对齐
546 }
547 stride_size = (stride_width * stride_height * 3) / 2; // 3 2 : NV12和NV21 size比例大小
548 }
549 }
550 GST_DEBUG_OBJECT(self, "format %s, stride width %u, stride height %u,"
551 "stride_size %u, info.size %" G_GSIZE_FORMAT,
552 format, stride_width, stride_height, stride_size, info.size);
553 if (stride_size > info.size) {
554 stride_size = info.size;
555 }
556 (void)fwrite(info.data, stride_size, 1, self->dump.dump_file);
557 (void)fflush(self->dump.dump_file);
558 gst_buffer_unmap(buffer, &info);
559 }
560
gst_surface_mem_sink_change_state(GstElement * element,GstStateChange transition)561 static GstStateChangeReturn gst_surface_mem_sink_change_state(GstElement *element, GstStateChange transition)
562 {
563 g_return_val_if_fail(element != nullptr, GST_STATE_CHANGE_FAILURE);
564 GstSurfaceMemSink *self = GST_SURFACE_MEM_SINK(element);
565
566 GST_DEBUG_OBJECT(element, "change state %d", transition);
567 switch (transition) {
568 case GST_STATE_CHANGE_READY_TO_PAUSED:
569 if (self->dump.enable_dump == TRUE) {
570 std::string dump_file = "/data/media/dump-" +
571 std::to_string(static_cast<int32_t>(FAKE_POINTER(element))) + ".yuv";
572 if (self->dump.dump_file == nullptr) {
573 self->dump.dump_file = fopen(dump_file.c_str(), "wb+");
574 }
575 }
576 break;
577 case GST_STATE_CHANGE_PAUSED_TO_READY:
578 if (self->dump.enable_dump == TRUE) {
579 if (self->dump.dump_file != nullptr) {
580 fclose(self->dump.dump_file);
581 self->dump.dump_file = nullptr;
582 }
583 }
584 break;
585 default:
586 break;
587 }
588 return GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
589 }
590