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