Skip to content

Commit b3f5ab6

Browse files
authored
feat: Throw an Error if Frame is already destroyed (#3099)
* feat: Throw an error if ObjC `Frame` is invalid * fix: Remove duplicate Frame closed checks
1 parent d670ca9 commit b3f5ab6

File tree

5 files changed

+50
-79
lines changed

5 files changed

+50
-79
lines changed

package/android/src/main/cpp/frameprocessors/FrameHostObject.cpp

+10-31
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,6 @@ std::vector<jsi::PropNameID> FrameHostObject::getPropertyNames(jsi::Runtime& rt)
5555
return result;
5656
}
5757

58-
jni::global_ref<JFrame> FrameHostObject::getFrame() {
59-
if (!_frame->getIsValid()) [[unlikely]] {
60-
throw std::runtime_error("Frame is already closed! "
61-
"Are you trying to access the Image data outside of a Frame Processor's lifetime?\n"
62-
"- If you want to use `console.log(frame)`, use `console.log(frame.toString())` instead.\n"
63-
"- If you want to do async processing, use `runAsync(...)` instead.\n"
64-
"- If you want to use runOnJS, increment it's ref-count: `frame.incrementRefCount()`");
65-
}
66-
return _frame;
67-
}
68-
6958
#define JSI_FUNC [=](jsi::Runtime & runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value
7059

7160
jsi::Value FrameHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& propName) {
@@ -76,40 +65,32 @@ jsi::Value FrameHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& pr
7665
return jsi::Value(_frame->getIsValid());
7766
}
7867
if (name == "width") {
79-
const auto& frame = getFrame();
80-
return jsi::Value(frame->getWidth());
68+
return jsi::Value(_frame->getWidth());
8169
}
8270
if (name == "height") {
83-
const auto& frame = getFrame();
84-
return jsi::Value(frame->getHeight());
71+
return jsi::Value(_frame->getHeight());
8572
}
8673
if (name == "isMirrored") {
87-
const auto& frame = getFrame();
88-
return jsi::Value(frame->getIsMirrored());
74+
return jsi::Value(_frame->getIsMirrored());
8975
}
9076
if (name == "orientation") {
91-
const auto& frame = getFrame();
92-
auto orientation = frame->getOrientation();
77+
auto orientation = _frame->getOrientation();
9378
auto string = orientation->getUnionValue();
9479
return jsi::String::createFromUtf8(runtime, string->toStdString());
9580
}
9681
if (name == "pixelFormat") {
97-
const auto& frame = getFrame();
98-
auto pixelFormat = frame->getPixelFormat();
82+
auto pixelFormat = _frame->getPixelFormat();
9983
auto string = pixelFormat->getUnionValue();
10084
return jsi::String::createFromUtf8(runtime, string->toStdString());
10185
}
10286
if (name == "timestamp") {
103-
const auto& frame = getFrame();
104-
return jsi::Value(static_cast<double>(frame->getTimestamp()));
87+
return jsi::Value(static_cast<double>(_frame->getTimestamp()));
10588
}
10689
if (name == "bytesPerRow") {
107-
const auto& frame = getFrame();
108-
return jsi::Value(frame->getBytesPerRow());
90+
return jsi::Value(_frame->getBytesPerRow());
10991
}
11092
if (name == "planesCount") {
111-
const auto& frame = getFrame();
112-
return jsi::Value(frame->getPlanesCount());
93+
return jsi::Value(_frame->getPlanesCount());
11394
}
11495

11596
// Internal Methods
@@ -134,8 +115,7 @@ jsi::Value FrameHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& pr
134115
if (name == "getNativeBuffer") {
135116
jsi::HostFunctionType getNativeBuffer = JSI_FUNC {
136117
#if __ANDROID_API__ >= 26
137-
const auto& frame = getFrame();
138-
AHardwareBuffer* hardwareBuffer = frame->getHardwareBuffer();
118+
AHardwareBuffer* hardwareBuffer = _frame->getHardwareBuffer();
139119
AHardwareBuffer_acquire(hardwareBuffer);
140120
uintptr_t pointer = reinterpret_cast<uintptr_t>(hardwareBuffer);
141121
jsi::HostFunctionType deleteFunc = [=](jsi::Runtime& runtime, const jsi::Value& thisArg, const jsi::Value* args,
@@ -159,8 +139,7 @@ jsi::Value FrameHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& pr
159139
if (name == "toArrayBuffer") {
160140
jsi::HostFunctionType toArrayBuffer = JSI_FUNC {
161141
#if __ANDROID_API__ >= 26
162-
const auto& frame = getFrame();
163-
AHardwareBuffer* hardwareBuffer = frame->getHardwareBuffer();
142+
AHardwareBuffer* hardwareBuffer = _frame->getHardwareBuffer();
164143
AHardwareBuffer_acquire(hardwareBuffer);
165144

166145
AHardwareBuffer_Desc bufferDescription;

package/android/src/main/cpp/frameprocessors/FrameHostObject.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ class JSI_EXPORT FrameHostObject : public jsi::HostObject, public std::enable_sh
2727
std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime& rt) override;
2828

2929
public:
30-
jni::global_ref<JFrame> getFrame();
30+
inline jni::global_ref<JFrame> getFrame() const noexcept {
31+
return _frame;
32+
}
3133

3234
private:
3335
jni::global_ref<JFrame> _frame;

package/ios/FrameProcessors/Frame.m

+19-10
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,28 @@ - (void)decrementRefCount {
3535
}
3636

3737
- (CMSampleBufferRef)buffer {
38+
if (!self.isValid) {
39+
@throw [[NSException alloc] initWithName:@"capture/frame-invalid"
40+
reason:@"Trying to access an already closed Frame! "
41+
"Are you trying to access the Image data outside of a Frame Processor's lifetime?\n"
42+
"- If you want to use `console.log(frame)`, use `console.log(frame.toString())` instead.\n"
43+
"- If you want to do async processing, use `runAsync(...)` instead.\n"
44+
"- If you want to use runOnJS, increment it's ref-count: `frame.incrementRefCount()`"
45+
userInfo:nil];
46+
}
3847
return _buffer;
3948
}
4049

50+
- (BOOL)isValid {
51+
return _buffer != nil && CFGetRetainCount(_buffer) > 0 && CMSampleBufferIsValid(_buffer);
52+
}
53+
4154
- (UIImageOrientation)orientation {
4255
return _orientation;
4356
}
4457

4558
- (NSString*)pixelFormat {
46-
CMFormatDescriptionRef format = CMSampleBufferGetFormatDescription(_buffer);
59+
CMFormatDescriptionRef format = CMSampleBufferGetFormatDescription(self.buffer);
4760
FourCharCode mediaType = CMFormatDescriptionGetMediaSubType(format);
4861
switch (mediaType) {
4962
case kCVPixelFormatType_32BGRA:
@@ -66,32 +79,28 @@ - (BOOL)isMirrored {
6679
return _isMirrored;
6780
}
6881

69-
- (BOOL)isValid {
70-
return _buffer != nil && CMSampleBufferIsValid(_buffer);
71-
}
72-
7382
- (size_t)width {
74-
CVPixelBufferRef imageBuffer = CMSampleBufferGetImageBuffer(_buffer);
83+
CVPixelBufferRef imageBuffer = CMSampleBufferGetImageBuffer(self.buffer);
7584
return CVPixelBufferGetWidth(imageBuffer);
7685
}
7786

7887
- (size_t)height {
79-
CVPixelBufferRef imageBuffer = CMSampleBufferGetImageBuffer(_buffer);
88+
CVPixelBufferRef imageBuffer = CMSampleBufferGetImageBuffer(self.buffer);
8089
return CVPixelBufferGetHeight(imageBuffer);
8190
}
8291

8392
- (double)timestamp {
84-
CMTime timestamp = CMSampleBufferGetPresentationTimeStamp(_buffer);
93+
CMTime timestamp = CMSampleBufferGetPresentationTimeStamp(self.buffer);
8594
return CMTimeGetSeconds(timestamp) * 1000.0;
8695
}
8796

8897
- (size_t)bytesPerRow {
89-
CVPixelBufferRef imageBuffer = CMSampleBufferGetImageBuffer(_buffer);
98+
CVPixelBufferRef imageBuffer = CMSampleBufferGetImageBuffer(self.buffer);
9099
return CVPixelBufferGetBytesPerRow(imageBuffer);
91100
}
92101

93102
- (size_t)planesCount {
94-
CVPixelBufferRef imageBuffer = CMSampleBufferGetImageBuffer(_buffer);
103+
CVPixelBufferRef imageBuffer = CMSampleBufferGetImageBuffer(self.buffer);
95104
return CVPixelBufferGetPlaneCount(imageBuffer);
96105
}
97106

package/ios/FrameProcessors/FrameHostObject.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ class JSI_EXPORT FrameHostObject : public jsi::HostObject, public std::enable_sh
2525
std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime& rt) override;
2626

2727
public:
28-
Frame* getFrame();
28+
inline Frame* getFrame() const noexcept {
29+
return _frame;
30+
}
2931

3032
private:
3133
Frame* _frame;

package/ios/FrameProcessors/FrameHostObject.mm

+15-36
Original file line numberDiff line numberDiff line change
@@ -40,60 +40,39 @@
4040
return result;
4141
}
4242

43-
Frame* FrameHostObject::getFrame() {
44-
if (!_frame.isValid) [[unlikely]] {
45-
throw std::runtime_error("Frame is already closed! "
46-
"Are you trying to access the Image data outside of a Frame Processor's lifetime?\n"
47-
"- If you want to use `console.log(frame)`, use `console.log(frame.toString())` instead.\n"
48-
"- If you want to do async processing, use `runAsync(...)` instead.\n"
49-
"- If you want to use runOnJS, increment it's ref-count: `frame.incrementRefCount()`");
50-
}
51-
return _frame;
52-
}
53-
5443
#define JSI_FUNC [=](jsi::Runtime & runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value
5544

5645
jsi::Value FrameHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& propName) {
5746
auto name = propName.utf8(runtime);
5847

5948
// Properties
6049
if (name == "width") {
61-
Frame* frame = getFrame();
62-
return jsi::Value((double)frame.width);
50+
return jsi::Value((double)_frame.width);
6351
}
6452
if (name == "height") {
65-
Frame* frame = getFrame();
66-
return jsi::Value((double)frame.height);
53+
return jsi::Value((double)_frame.height);
6754
}
6855
if (name == "orientation") {
69-
Frame* frame = getFrame();
70-
NSString* orientation = [NSString stringWithParsed:frame.orientation];
56+
NSString* orientation = [NSString stringWithParsed:_frame.orientation];
7157
return jsi::String::createFromUtf8(runtime, orientation.UTF8String);
7258
}
7359
if (name == "isMirrored") {
74-
Frame* frame = getFrame();
75-
return jsi::Value(frame.isMirrored);
60+
return jsi::Value(_frame.isMirrored);
7661
}
7762
if (name == "timestamp") {
78-
Frame* frame = getFrame();
79-
return jsi::Value(frame.timestamp);
63+
return jsi::Value(_frame.timestamp);
8064
}
8165
if (name == "pixelFormat") {
82-
Frame* frame = getFrame();
83-
return jsi::String::createFromUtf8(runtime, frame.pixelFormat.UTF8String);
66+
return jsi::String::createFromUtf8(runtime, _frame.pixelFormat.UTF8String);
8467
}
8568
if (name == "isValid") {
86-
// unsafely access the Frame and try to see if it's valid
87-
Frame* frame = _frame;
88-
return jsi::Value(frame != nil && frame.isValid);
69+
return jsi::Value(_frame != nil && _frame.isValid);
8970
}
9071
if (name == "bytesPerRow") {
91-
Frame* frame = getFrame();
92-
return jsi::Value((double)frame.bytesPerRow);
72+
return jsi::Value((double)_frame.bytesPerRow);
9373
}
9474
if (name == "planesCount") {
95-
Frame* frame = getFrame();
96-
return jsi::Value((double)frame.planesCount);
75+
return jsi::Value((double)_frame.planesCount);
9776
}
9877

9978
// Internal methods
@@ -116,8 +95,7 @@
11695
if (name == "getNativeBuffer") {
11796
auto getNativeBuffer = JSI_FUNC {
11897
// Box-cast to uintptr (just 64-bit address)
119-
Frame* frame = getFrame();
120-
CMSampleBufferRef sampleBuffer = frame.buffer;
98+
CMSampleBufferRef sampleBuffer = _frame.buffer;
12199
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
122100
uintptr_t pointer = reinterpret_cast<uintptr_t>(pixelBuffer);
123101
jsi::HostFunctionType deleteFunc = [=](jsi::Runtime& runtime, const jsi::Value& thisArg, const jsi::Value* args,
@@ -137,8 +115,7 @@
137115
if (name == "toArrayBuffer") {
138116
auto toArrayBuffer = JSI_FUNC {
139117
// Get CPU readable Pixel Buffer from Frame and write it to a jsi::ArrayBuffer
140-
Frame* frame = getFrame();
141-
auto pixelBuffer = CMSampleBufferGetImageBuffer(frame.buffer);
118+
auto pixelBuffer = CMSampleBufferGetImageBuffer(_frame.buffer);
142119
auto bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer);
143120
auto height = CVPixelBufferGetHeight(pixelBuffer);
144121

@@ -172,8 +149,10 @@
172149
if (name == "toString") {
173150
auto toString = JSI_FUNC {
174151
// Print debug description (width, height)
175-
Frame* frame = getFrame();
176-
NSMutableString* string = [NSMutableString stringWithFormat:@"%lu x %lu %@ Frame", frame.width, frame.height, frame.pixelFormat];
152+
if (!_frame.isValid) {
153+
return jsi::String::createFromUtf8(runtime, "[closed frame]");
154+
}
155+
NSMutableString* string = [NSMutableString stringWithFormat:@"%lu x %lu %@ Frame", _frame.width, _frame.height, _frame.pixelFormat];
177156
return jsi::String::createFromUtf8(runtime, string.UTF8String);
178157
};
179158
return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "toString"), 0, toString);

0 commit comments

Comments
 (0)