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