Skip to content

Commit 0bfa152

Browse files
committed
fix: implement CVPixelBufferPool for macOS
1 parent edcfd1b commit 0bfa152

File tree

3 files changed

+153
-0
lines changed

3 files changed

+153
-0
lines changed

shared/darwin/AgoraCVPixelBufferUtils.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22

33
@interface AgoraCVPixelBufferUtils : NSObject
44

5+
/**
6+
* Frees all CVPixelBuffer pools.
7+
*
8+
* We use a pool of CVPixelBuffers to avoid allocating and freeing them
9+
* frequently. This method allows us to free all the CVPixelBuffers in the
10+
* pool when needed.
11+
*/
12+
+ (void)freeAllCVPixelBufferPools;
13+
514
/**
615
* Creates a copy of a CVPixelBuffer.
716
*

shared/darwin/AgoraCVPixelBufferUtils.mm

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,132 @@
11
#import "AgoraCVPixelBufferUtils.h"
2+
3+
#include <mutex>
4+
#include <tuple>
5+
#include <unordered_map>
6+
27
#include <CoreVideo/CoreVideo.h>
8+
#import <Foundation/Foundation.h>
9+
10+
#pragma mark - AgoraCVPixelBufferPool
11+
12+
struct AgoraCVPixelBufferPoolKey {
13+
OSType type;
14+
size_t width;
15+
size_t height;
16+
17+
bool operator==(const AgoraCVPixelBufferPoolKey &other) const {
18+
return type == other.type && width == other.width && height == other.height;
19+
}
20+
21+
bool operator<(const AgoraCVPixelBufferPoolKey &other) const {
22+
return std::tie(type, width, height) <
23+
std::tie(other.type, other.width, other.height);
24+
}
25+
};
26+
27+
struct AgoraCVPixelBufferPoolKeyHash {
28+
std::size_t operator()(const AgoraCVPixelBufferPoolKey &key) const {
29+
return std::hash<OSType>()(key.type) ^ std::hash<size_t>()(key.width) ^
30+
std::hash<size_t>()(key.height);
31+
}
32+
};
33+
34+
@interface AgoraCVPixelBufferPool : NSObject
35+
36+
+ (instancetype)sharedInstance;
37+
38+
- (CVPixelBufferRef)createPixelBuffer:(OSType)type
39+
width:(size_t)width
40+
height:(size_t)height;
41+
42+
- (void)freeAllCVPixelBufferPools;
43+
44+
@end
45+
46+
@implementation AgoraCVPixelBufferPool {
47+
std::mutex _mutex;
48+
std::unordered_map<AgoraCVPixelBufferPoolKey, CVPixelBufferPoolRef,
49+
AgoraCVPixelBufferPoolKeyHash>
50+
_pools;
51+
}
52+
53+
+ (instancetype)sharedInstance {
54+
static AgoraCVPixelBufferPool *instance = nil;
55+
56+
// should we use this or just use a static global variable?
57+
static dispatch_once_t onceToken;
58+
dispatch_once(&onceToken, ^{
59+
instance = [[AgoraCVPixelBufferPool alloc] init];
60+
});
61+
62+
return instance;
63+
}
64+
65+
- (CVPixelBufferRef)createPixelBuffer:(OSType)type
66+
width:(size_t)width
67+
height:(size_t)height {
68+
std::lock_guard<std::mutex> lock(_mutex);
69+
CVPixelBufferPoolRef pool = nil;
70+
71+
AgoraCVPixelBufferPoolKey key = {type, width, height};
72+
auto it = _pools.find(key);
73+
if (it == _pools.end()) {
74+
NSMutableDictionary *attributes =
75+
[NSMutableDictionary dictionaryWithDictionary:@{
76+
(id)kCVPixelBufferIOSurfacePropertiesKey : @{},
77+
#if defined(WEBRTC_IOS)
78+
(id)kCVPixelBufferOpenGLESCompatibilityKey : @YES,
79+
#elif defined(WEBRTC_MAC)
80+
(id)kCVPixelBufferOpenGLCompatibilityKey : @YES,
81+
#endif
82+
(id)kCVPixelBufferCGImageCompatibilityKey : @YES,
83+
(id)kCVPixelBufferCGBitmapContextCompatibilityKey : @YES,
84+
(id)kCVPixelBufferPixelFormatTypeKey : @(key.type),
85+
(id)kCVPixelBufferWidthKey : @(key.width),
86+
(id)kCVPixelBufferHeightKey : @(key.height)
87+
}];
88+
89+
if (@available(macOS 10.11, *)) {
90+
[attributes setObject:@YES
91+
forKey:(id)kCVPixelBufferMetalCompatibilityKey];
92+
}
93+
CVReturn status =
94+
CVPixelBufferPoolCreate(kCFAllocatorDefault, nullptr,
95+
(__bridge CFDictionaryRef)attributes, &pool);
96+
if (status != kCVReturnSuccess || !pool) {
97+
return nil;
98+
}
99+
100+
_pools[key] = pool;
101+
} else {
102+
pool = it->second;
103+
}
104+
105+
CVPixelBufferRef pixelBuffer = nil;
106+
CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pool, &pixelBuffer);
107+
108+
return pixelBuffer;
109+
}
110+
111+
- (void)freeAllCVPixelBufferPools {
112+
std::lock_guard<std::mutex> lock(_mutex);
113+
for (auto &pool : _pools) {
114+
CVPixelBufferPoolFlush(pool.second, kCVPixelBufferPoolFlushExcessBuffers);
115+
CVPixelBufferPoolRelease(pool.second);
116+
}
117+
_pools.clear();
118+
}
119+
120+
@end
121+
122+
#pragma mark - AgoraCVPixelBufferUtils
3123

4124
@implementation AgoraCVPixelBufferUtils
5125

126+
+ (void)freeAllCVPixelBufferPools {
127+
[[AgoraCVPixelBufferPool sharedInstance] freeAllCVPixelBufferPools];
128+
}
129+
6130
+ (BOOL)copyNV12CVPixelBuffer:(CVPixelBufferRef _Nonnull)sourcePixelBuffer
7131
destPixelBuffer:(CVPixelBufferRef _Nonnull)destPixelBuffer {
8132
if (!sourcePixelBuffer || !destPixelBuffer) {
@@ -116,6 +240,8 @@ + (CVPixelBufferRef)copyCVPixelBuffer:
116240
size_t sourceHeight = CVPixelBufferGetHeight(sourcePixelBuffer);
117241
OSType sourceFormat = CVPixelBufferGetPixelFormatType(sourcePixelBuffer);
118242

243+
// create pixel buffer directly
244+
#if 1
119245
// Create new buffer with Metal compatibility
120246
NSDictionary *pixelBufferAttributes = @{
121247
// This key is required to generate SKPicture with CVPixelBufferRef in
@@ -130,6 +256,16 @@ + (CVPixelBufferRef)copyCVPixelBuffer:
130256
if (status != kCVReturnSuccess || !destPixelBuffer) {
131257
return nil;
132258
}
259+
#else
260+
// create pixel buffer from pool
261+
CVPixelBufferRef destPixelBuffer =
262+
[[AgoraCVPixelBufferPool sharedInstance] createPixelBuffer:sourceFormat
263+
width:sourceWidth
264+
height:sourceHeight];
265+
if (!destPixelBuffer) {
266+
return nil;
267+
}
268+
#endif
133269

134270
// Copy data based on format
135271
BOOL success = NO;

shared/darwin/VideoViewController.mm

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#import <Foundation/Foundation.h>
22
#import "VideoViewController.h"
33
#import "TextureRenderer.h"
4+
#import "AgoraCVPixelBufferUtils.h"
5+
46
#import <AgoraRtcWrapper/iris_engine_base.h>
57
#import <AgoraRtcWrapper/iris_rtc_rendering_cxx.h>
68

@@ -234,8 +236,14 @@ - (BOOL)destroyTextureRender:(int64_t)textureId {
234236
if (textureRender != nil) {
235237
[textureRender dispose];
236238
[self.textureRenders removeObjectForKey:@(textureId)];
239+
240+
if (self.textureRenders.count == 0) {
241+
[AgoraCVPixelBufferUtils freeAllCVPixelBufferPools];
242+
}
243+
237244
return YES;
238245
}
246+
239247
return NO;
240248
}
241249

0 commit comments

Comments
 (0)