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_allocator.h"
17 #include <sync_fence.h>
18 #include "media_log.h"
19 #include "media_dfx.h"
20 #include "player_xcollie.h"
21
22 GST_DEBUG_CATEGORY_STATIC(gst_surface_allocator_debug_category);
23 #define GST_CAT_DEFAULT gst_surface_allocator_debug_category
24
25 #define gst_surface_allocator_parent_class parent_class
26 G_DEFINE_TYPE(GstSurfaceAllocator, gst_surface_allocator, GST_TYPE_ALLOCATOR);
27
28 #define GST_SURFACE_ALLOCATOR_LOCK(allocator) (g_mutex_lock(&(allocator)->lock))
29 #define GST_SURFACE_ALLOCATOR_UNLOCK(allocator) (g_mutex_unlock(&(allocator)->lock))
30 #define GST_SURFACE_ALLOCATOR_WAIT(allocator) (g_cond_wait(&(allocator)->cond, &(allocator)->lock))
31 #define GST_SURFACE_ALLOCATOR_NOTIFY(allocator) (g_cond_signal(&(allocator)->cond))
32
33 using namespace OHOS;
34 using namespace std;
35 using namespace OHOS::Media;
36
37 enum class VideoScaleType {
38 VIDEO_SCALE_TYPE_FIT,
39 VIDEO_SCALE_TYPE_FIT_CROP,
40 VIDEO_SCALE_TYPE_LAST,
41 };
42
43 namespace {
44 const std::unordered_map<VideoScaleType, OHOS::ScalingMode> SCALEMODE_MAP = {
45 { VideoScaleType::VIDEO_SCALE_TYPE_FIT, OHOS::SCALING_MODE_SCALE_TO_WINDOW },
46 { VideoScaleType::VIDEO_SCALE_TYPE_FIT_CROP, OHOS::SCALING_MODE_SCALE_CROP},
47 };
48 }
49
50 #define SURFACE_BUFFER_LOG_INFO(allocator, id) \
51 GST_DEBUG_OBJECT(allocator, \
52 "buffer %u requestBuffer %d flushBuffer %d cacheBuffer %d totalBuffer %d", \
53 id, allocator->requestBufferNum, allocator->flushBufferNum, \
54 allocator->cacheBufferNum, allocator->totalBufferNum)
55
gst_surface_allocator_buffer_release(GstSurfaceAllocator * allocator,sptr<SurfaceBuffer> & buffer)56 static void gst_surface_allocator_buffer_release(GstSurfaceAllocator *allocator, sptr<SurfaceBuffer> &buffer)
57 {
58 GST_DEBUG_OBJECT(allocator, "buffer released");
59 (void)buffer;
60 GST_SURFACE_ALLOCATOR_LOCK(allocator);
61 allocator->flushBufferNum--;
62 allocator->cacheBufferNum++;
63 SURFACE_BUFFER_LOG_INFO(allocator, 0);
64 GST_SURFACE_ALLOCATOR_NOTIFY(allocator);
65 GST_SURFACE_ALLOCATOR_UNLOCK(allocator);
66 MediaTrace::CounterTrace("flushBufferNum", allocator->flushBufferNum);
67 MediaTrace::CounterTrace("cacheBufferNum", allocator->cacheBufferNum);
68 }
69
OnBufferReleased(sptr<SurfaceBuffer> & buffer)70 GSError AllocatorWrap::OnBufferReleased(sptr<SurfaceBuffer> &buffer)
71 {
72 gst_surface_allocator_buffer_release(&owner_, buffer);
73 return OHOS::SurfaceError::SURFACE_ERROR_OK;
74 }
75
gst_surface_allocator_set_surface(GstSurfaceAllocator * allocator,OHOS::sptr<OHOS::Surface> surface)76 gboolean gst_surface_allocator_set_surface(GstSurfaceAllocator *allocator, OHOS::sptr<OHOS::Surface> surface)
77 {
78 if (allocator == nullptr) {
79 GST_ERROR("allocator is nullptr");
80 return FALSE;
81 }
82 if (surface == nullptr) {
83 GST_ERROR("surface is nullptr");
84 return FALSE;
85 }
86 allocator->surface = surface;
87 if (allocator->allocatorWrap) {
88 delete allocator->allocatorWrap;
89 allocator->allocatorWrap = nullptr;
90 }
91 allocator->allocatorWrap = new AllocatorWrap(*allocator);
92 allocator->clean = FALSE;
93 GST_SURFACE_ALLOCATOR_LOCK(allocator);
94 allocator->requestBufferNum = 0;
95 allocator->totalBufferNum = 0;
96 allocator->cacheBufferNum = 0;
97 allocator->flushBufferNum = 0;
98 GST_SURFACE_ALLOCATOR_UNLOCK(allocator);
99 auto bufferReleased = std::bind(&AllocatorWrap::OnBufferReleased, allocator->allocatorWrap, std::placeholders::_1);
100 GSError ret = OHOS::SurfaceError::SURFACE_ERROR_OK;
101 LISTENER(ret = surface->RegisterReleaseListener(bufferReleased),
102 "surface::RegisterReleaseListener", PlayerXCollie::timerTimeout)
103
104 if (ret != OHOS::SurfaceError::SURFACE_ERROR_OK) {
105 allocator->isCallbackMode = FALSE;
106 GST_ERROR("Register Release Listener failed");
107 }
108 return TRUE;
109 }
110
gst_surface_allocator_get_scale_type(GstSurfaceAllocParam param)111 static OHOS::ScalingMode gst_surface_allocator_get_scale_type(GstSurfaceAllocParam param)
112 {
113 if (SCALEMODE_MAP.find(static_cast<VideoScaleType>(param.scale_type)) == SCALEMODE_MAP.end()) {
114 return OHOS::SCALING_MODE_SCALE_TO_WINDOW;
115 }
116 return SCALEMODE_MAP.at(static_cast<VideoScaleType>(param.scale_type));
117 }
118
gst_surface_cancel_buffer(GstSurfaceAllocator * allocator,OHOS::sptr<OHOS::SurfaceBuffer> & buffer)119 static void gst_surface_cancel_buffer(GstSurfaceAllocator *allocator, OHOS::sptr<OHOS::SurfaceBuffer> &buffer)
120 {
121 MediaTrace scaleTrace("Surface::CancelBuffer");
122 LISTENER(allocator->surface->CancelBuffer(buffer),
123 "surface::CancelBuffer", PlayerXCollie::timerTimeout)
124 GST_SURFACE_ALLOCATOR_LOCK(allocator);
125 allocator->requestBufferNum--;
126 allocator->cacheBufferNum++;
127 SURFACE_BUFFER_LOG_INFO(allocator, buffer->GetSeqNum());
128 GST_SURFACE_ALLOCATOR_UNLOCK(allocator);
129 MediaTrace::CounterTrace("requestBufferNum", allocator->requestBufferNum);
130 MediaTrace::CounterTrace("cacheBufferNum", allocator->cacheBufferNum);
131 }
132
gst_surface_scale_buffer(GstSurfaceAllocator * allocator,GstSurfaceAllocParam param,OHOS::sptr<OHOS::SurfaceBuffer> & buffer)133 static bool gst_surface_scale_buffer(GstSurfaceAllocator *allocator, GstSurfaceAllocParam param,
134 OHOS::sptr<OHOS::SurfaceBuffer> &buffer)
135 {
136 MediaTrace scaleTrace("Surface::SetScalingMode");
137 auto scaleType = gst_surface_allocator_get_scale_type(param);
138 OHOS::SurfaceError ret = OHOS::SurfaceError::SURFACE_ERROR_OK;
139 LISTENER(ret = allocator->surface->SetScalingMode(buffer->GetSeqNum(), scaleType),
140 "surface::SetScalingMode", PlayerXCollie::timerTimeout)
141 if (ret != OHOS::SurfaceError::SURFACE_ERROR_OK) {
142 GST_ERROR("surface buffer set scaling mode failed");
143 gst_surface_cancel_buffer(allocator, buffer);
144 return false;
145 }
146 return true;
147 }
148
gst_surface_map_buffer(GstSurfaceAllocator * allocator,OHOS::sptr<OHOS::SurfaceBuffer> & buffer)149 static bool gst_surface_map_buffer(GstSurfaceAllocator *allocator, OHOS::sptr<OHOS::SurfaceBuffer> &buffer)
150 {
151 MediaTrace mapTrace("Surface::Map");
152 if (buffer->Map() != OHOS::SurfaceError::SURFACE_ERROR_OK) {
153 GST_ERROR("surface buffer Map failed");
154 gst_surface_cancel_buffer(allocator, buffer);
155 return false;
156 }
157 return true;
158 }
159
gst_surface_request_buffer(GstSurfaceAllocator * allocator,GstSurfaceAllocParam param,OHOS::sptr<OHOS::SurfaceBuffer> & buffer)160 static bool gst_surface_request_buffer(GstSurfaceAllocator *allocator, GstSurfaceAllocParam param,
161 OHOS::sptr<OHOS::SurfaceBuffer> &buffer)
162 {
163 {
164 GST_SURFACE_ALLOCATOR_LOCK(allocator);
165 while (allocator->cacheBufferNum == 0 && allocator->clean == FALSE && allocator->isCallbackMode) {
166 GST_SURFACE_ALLOCATOR_WAIT(allocator);
167 }
168 GST_SURFACE_ALLOCATOR_UNLOCK(allocator);
169 }
170 if (allocator->clean == TRUE) {
171 return FALSE;
172 }
173
174 static constexpr int32_t stride_alignment = 8;
175 int32_t wait_time = (param.dont_wait && allocator->isCallbackMode) ? 0 : INT_MAX; // wait forever or no wait.
176 OHOS::BufferRequestConfig request_config = {
177 param.width, param.height, stride_alignment, param.format,
178 param.usage, wait_time
179 };
180 int32_t release_fence = -1;
181 OHOS::SurfaceError ret = OHOS::SurfaceError::SURFACE_ERROR_OK;
182 {
183 MediaTrace trace("Surface::RequestBuffer");
184 if (wait_time == 0) {
185 LISTENER(ret = allocator->surface->RequestBuffer(buffer, release_fence, request_config),
186 "surface::RequestBuffer", PlayerXCollie::timerTimeout)
187 } else {
188 ret = allocator->surface->RequestBuffer(buffer, release_fence, request_config);
189 }
190 if (ret != OHOS::SurfaceError::SURFACE_ERROR_OK || buffer == nullptr) {
191 GST_ERROR("there is no more surface buffer");
192 return false;
193 }
194 }
195 {
196 GST_SURFACE_ALLOCATOR_LOCK(allocator);
197 allocator->requestBufferNum++;
198 allocator->cacheBufferNum--;
199 SURFACE_BUFFER_LOG_INFO(allocator, buffer->GetSeqNum());
200 GST_SURFACE_ALLOCATOR_UNLOCK(allocator);
201 MediaTrace::CounterTrace("requestBufferNum", allocator->requestBufferNum);
202 MediaTrace::CounterTrace("cacheBufferNum", allocator->cacheBufferNum);
203 }
204
205 g_return_val_if_fail(gst_surface_map_buffer(allocator, buffer), false);
206
207 {
208 MediaTrace FenceTrace("Surface::WaitFence");
209 OHOS::sptr<OHOS::SyncFence> autoFence = new(std::nothrow) OHOS::SyncFence(release_fence);
210 if (autoFence != nullptr) {
211 autoFence->Wait(100); // 100ms
212 }
213 }
214
215 g_return_val_if_fail(gst_surface_scale_buffer(allocator, param, buffer), false);
216 return true;
217 }
218
gst_surface_allocator_alloc(GstSurfaceAllocator * allocator,GstSurfaceAllocParam param)219 GstSurfaceMemory *gst_surface_allocator_alloc(GstSurfaceAllocator *allocator, GstSurfaceAllocParam param)
220 {
221 g_return_val_if_fail(allocator != nullptr && allocator->surface != nullptr, nullptr);
222
223 OHOS::sptr<OHOS::SurfaceBuffer> buffer = nullptr;
224 if (!gst_surface_request_buffer(allocator, param, buffer) || buffer == nullptr) {
225 GST_ERROR("failed to request surface buffer");
226 return nullptr;
227 }
228
229 GstSurfaceMemory *memory = reinterpret_cast<GstSurfaceMemory *>(g_slice_alloc0(sizeof(GstSurfaceMemory)));
230 if (memory == nullptr) {
231 GST_ERROR("alloc GstSurfaceMemory slice failed");
232 LISTENER(allocator->surface->CancelBuffer(buffer),
233 "surface::CancelBuffer", PlayerXCollie::timerTimeout)
234 return nullptr;
235 }
236
237 gst_memory_init(GST_MEMORY_CAST(memory), (GstMemoryFlags)0, GST_ALLOCATOR_CAST(allocator), nullptr,
238 buffer->GetSize(), 0, 0, buffer->GetSize());
239
240 memory->buf = buffer;
241 memory->fence = -1;
242 memory->need_render = FALSE;
243 GST_DEBUG("alloc surface buffer for width: %d, height: %d, format: %d, size: %u",
244 param.width, param.height, param.format, buffer->GetSize());
245
246 return memory;
247 }
248
gst_surface_allocator_free(GstAllocator * baseAllocator,GstMemory * baseMemory)249 static void gst_surface_allocator_free(GstAllocator *baseAllocator, GstMemory *baseMemory)
250 {
251 GstSurfaceAllocator *allocator = reinterpret_cast<GstSurfaceAllocator*>(baseAllocator);
252 GstSurfaceMemory *memory = reinterpret_cast<GstSurfaceMemory*>(baseMemory);
253 g_return_if_fail(memory != nullptr && allocator != nullptr && allocator->surface != nullptr);
254
255 GST_DEBUG("free surface buffer %u for width: %d, height: %d, format: %d, size: %u, need_render: %d, fence: %d",
256 memory->buf->GetSeqNum(), memory->buf->GetWidth(), memory->buf->GetHeight(), memory->buf->GetFormat(),
257 memory->buf->GetSize(), memory->need_render, memory->fence);
258
259 if (!memory->need_render) {
260 MediaTrace trace("Surface::CancelBuffer");
261 OHOS::SurfaceError ret = OHOS::SurfaceError::SURFACE_ERROR_OK;
262 LISTENER(ret = allocator->surface->CancelBuffer(memory->buf),
263 "surface::CancelBuffer", PlayerXCollie::timerTimeout)
264 if (ret != OHOS::SurfaceError::SURFACE_ERROR_OK) {
265 GST_INFO("cancel buffer to surface failed, %d", ret);
266 } else {
267 GST_SURFACE_ALLOCATOR_LOCK(allocator);
268 allocator->requestBufferNum--;
269 allocator->cacheBufferNum++;
270 SURFACE_BUFFER_LOG_INFO(allocator, memory->buf->GetSeqNum());
271 GST_SURFACE_ALLOCATOR_NOTIFY(allocator);
272 GST_SURFACE_ALLOCATOR_UNLOCK(allocator);
273 MediaTrace::CounterTrace("requestBufferNum", allocator->requestBufferNum);
274 MediaTrace::CounterTrace("cacheBufferNum", allocator->cacheBufferNum);
275 }
276 }
277
278 memory->buf = nullptr;
279 g_slice_free(GstSurfaceMemory, memory);
280 }
281
gst_surface_allocator_alloc_dummy(GstAllocator * allocator,gsize size,GstAllocationParams * params)282 static GstMemory *gst_surface_allocator_alloc_dummy(GstAllocator *allocator, gsize size, GstAllocationParams *params)
283 {
284 (void)allocator;
285 (void)size;
286 (void)params;
287 return nullptr;
288 }
289
gst_surface_allocator_mem_map(GstMemory * mem,gsize maxsize,GstMapFlags flags)290 static gpointer gst_surface_allocator_mem_map(GstMemory *mem, gsize maxsize, GstMapFlags flags)
291 {
292 (void)maxsize;
293 (void)flags;
294 g_return_val_if_fail(mem != nullptr, nullptr);
295 g_return_val_if_fail(gst_is_surface_memory(mem), nullptr);
296
297 GstSurfaceMemory *sf_mem = reinterpret_cast<GstSurfaceMemory *>(mem);
298 g_return_val_if_fail(sf_mem->buf != nullptr, nullptr);
299
300 return sf_mem->buf->GetVirAddr();
301 }
302
gst_surface_allocator_mem_unmap(GstMemory * mem)303 static void gst_surface_allocator_mem_unmap(GstMemory *mem)
304 {
305 (void)mem;
306 }
307
gst_surface_allocator_flush_buffer(GstSurfaceAllocator * allocator,sptr<SurfaceBuffer> & buffer,int32_t fence,BufferFlushConfig & config)308 gboolean gst_surface_allocator_flush_buffer(GstSurfaceAllocator *allocator, sptr<SurfaceBuffer>& buffer,
309 int32_t fence, BufferFlushConfig &config)
310 {
311 if (allocator->surface) {
312 GST_DEBUG_OBJECT(allocator, "FlushBuffer");
313 MediaTrace trace("Surface::FlushBuffer");
314 OHOS::SurfaceError ret = OHOS::SurfaceError::SURFACE_ERROR_OK;
315 LISTENER(ret = allocator->surface->FlushBuffer(buffer, fence, config),
316 "surface::FlushBuffer", PlayerXCollie::timerTimeout)
317 if (ret != OHOS::SurfaceError::SURFACE_ERROR_OK) {
318 GST_ERROR_OBJECT(allocator, "flush buffer %d to surface failed, %d", buffer->GetSeqNum(), ret);
319 return FALSE;
320 }
321 }
322 {
323 GST_SURFACE_ALLOCATOR_LOCK(allocator);
324 allocator->flushBufferNum++;
325 allocator->requestBufferNum--;
326 SURFACE_BUFFER_LOG_INFO(allocator, buffer->GetSeqNum());
327 GST_SURFACE_ALLOCATOR_UNLOCK(allocator);
328 MediaTrace::CounterTrace("flushBufferNum", allocator->flushBufferNum);
329 MediaTrace::CounterTrace("requestBufferNum", allocator->requestBufferNum);
330 }
331 return TRUE;
332 }
333
gst_surface_allocator_set_queue_size(GstSurfaceAllocator * allocator,int32_t size)334 gboolean gst_surface_allocator_set_queue_size(GstSurfaceAllocator *allocator, int32_t size)
335 {
336 if (allocator->surface) {
337 GST_DEBUG_OBJECT(allocator, "set queue size %d", size);
338 MediaTrace trace("Surface::SetQueueSize");
339 OHOS::SurfaceError err = OHOS::SurfaceError::SURFACE_ERROR_OK;
340 LISTENER(err = allocator->surface->SetQueueSize(size),
341 "surface::SetQueueSize", PlayerXCollie::timerTimeout)
342 if (err != OHOS::SurfaceError::SURFACE_ERROR_OK) {
343 GST_ERROR_OBJECT(allocator, "set queue size failed, %d", err);
344 return FALSE;
345 }
346 }
347 {
348 GST_SURFACE_ALLOCATOR_LOCK(allocator);
349 allocator->cacheBufferNum += (size - allocator->totalBufferNum);
350 allocator->totalBufferNum = size;
351 SURFACE_BUFFER_LOG_INFO(allocator, 0);
352 GST_SURFACE_ALLOCATOR_NOTIFY(allocator);
353 GST_SURFACE_ALLOCATOR_UNLOCK(allocator);
354 MediaTrace::CounterTrace("cacheBufferNum", allocator->cacheBufferNum);
355 MediaTrace::CounterTrace("totalBufferNum", allocator->totalBufferNum);
356 }
357 return TRUE;
358 }
359
gst_surface_allocator_clean_cache(GstSurfaceAllocator * allocator)360 void gst_surface_allocator_clean_cache(GstSurfaceAllocator *allocator)
361 {
362 GST_INFO_OBJECT(allocator, "clean cache");
363 if (allocator->surface) {
364 OHOS::SurfaceError err = OHOS::SurfaceError::SURFACE_ERROR_OK;
365 MediaTrace trace("Surface::CleanCache");
366 LISTENER(err = allocator->surface->CleanCache(),
367 "surface::CleanCache", PlayerXCollie::timerTimeout)
368 if (err != OHOS::SurfaceError::SURFACE_ERROR_OK) {
369 GST_ERROR_OBJECT(allocator, "clean cache failed, %d", err);
370 }
371 }
372 allocator->clean = TRUE;
373 GST_SURFACE_ALLOCATOR_NOTIFY(allocator);
374 }
375
gst_surface_allocator_init(GstSurfaceAllocator * allocator)376 static void gst_surface_allocator_init(GstSurfaceAllocator *allocator)
377 {
378 GstAllocator *base_allocator = GST_ALLOCATOR_CAST(allocator);
379 g_return_if_fail(base_allocator != nullptr);
380
381 GST_DEBUG_OBJECT(allocator, "init allocator 0x%06" PRIXPTR "", FAKE_POINTER(allocator));
382
383 base_allocator->mem_type = GST_SURFACE_MEMORY_TYPE;
384 base_allocator->mem_map = (GstMemoryMapFunction)gst_surface_allocator_mem_map;
385 base_allocator->mem_unmap = (GstMemoryUnmapFunction)gst_surface_allocator_mem_unmap;
386 allocator->allocatorWrap = nullptr;
387 allocator->isCallbackMode = TRUE;
388 g_mutex_init(&allocator->lock);
389 g_cond_init(&allocator->cond);
390 }
391
gst_surface_allocator_finalize(GObject * obj)392 static void gst_surface_allocator_finalize(GObject *obj)
393 {
394 GstSurfaceAllocator *allocator = GST_SURFACE_ALLOCATOR(obj);
395 g_return_if_fail(allocator != nullptr);
396
397 allocator->surface = nullptr;
398 if (allocator->allocatorWrap) {
399 delete allocator->allocatorWrap;
400 allocator->allocatorWrap = nullptr;
401 }
402 g_mutex_clear(&allocator->lock);
403 g_cond_clear(&allocator->cond);
404 GST_DEBUG_OBJECT(allocator, "finalize allocator 0x%06" PRIXPTR "", FAKE_POINTER(allocator));
405 G_OBJECT_CLASS(parent_class)->finalize(obj);
406 }
407
gst_surface_allocator_class_init(GstSurfaceAllocatorClass * klass)408 static void gst_surface_allocator_class_init(GstSurfaceAllocatorClass *klass)
409 {
410 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
411 g_return_if_fail(gobject_class != nullptr);
412
413 gobject_class->finalize = gst_surface_allocator_finalize;
414
415 GstAllocatorClass *allocatorClass = GST_ALLOCATOR_CLASS(klass);
416 g_return_if_fail(allocatorClass != nullptr);
417
418 allocatorClass->alloc = gst_surface_allocator_alloc_dummy;
419 allocatorClass->free = gst_surface_allocator_free;
420 GST_DEBUG_CATEGORY_INIT(gst_surface_allocator_debug_category, "prosurallocator", 0, "surface allocator");
421 }
422
gst_surface_allocator_new()423 GstSurfaceAllocator *gst_surface_allocator_new()
424 {
425 GstSurfaceAllocator *alloc = GST_SURFACE_ALLOCATOR(g_object_new(
426 GST_TYPE_SURFACE_ALLOCATOR, "name", "SurfaceAllocator", nullptr));
427 (void)gst_object_ref_sink(alloc);
428
429 return alloc;
430 }
431