Skip to content

Commit effa97f

Browse files
committed
CogVM source as per VMMaker.oscog-eem.2949
Add CameraPlugin primSetCameraBuffers to avoid a copy when reading a frame. Provide an implementation for macOS.
1 parent 5a7aa38 commit effa97f

File tree

5 files changed

+231
-41
lines changed

5 files changed

+231
-41
lines changed

platforms/Cross/plugins/CameraPlugin/CameraPlugin.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ sqInt CameraGetFrame(sqInt cameraNum, unsigned char *buf, sqInt pixelCount);
1212
sqInt CameraGetParam(sqInt cameraNum, sqInt paramNum);
1313
sqInt CameraGetSemaphore(sqInt cameraNum);
1414
sqInt CameraSetSemaphore(sqInt cameraNum, sqInt semaphoreIndex);
15+
sqInt CameraSetFrameBuffers(sqInt cameraNum, sqInt bufferA, sqInt bufferBorNil);
1516

1617
#endif /* _SQ_CAMERA_PLUGIN_H_ */

platforms/iOS/plugins/CameraPlugin/AVFoundationVideoGrabber.m

Lines changed: 107 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
*/
1212

1313
#include "sqVirtualMachine.h"
14+
#include "sqMemoryFence.h"
1415
#include "CameraPlugin.h"
1516
extern struct VirtualMachine *interpreterProxy;
1617

@@ -27,8 +28,8 @@
2728
#endif
2829

2930
#if defined(MAC_OS_X_VERSION_10_14)
30-
static canAccessCamera = false;
31-
static askedToAccessCamera = false;
31+
static unsigned char canAccessCamera = false;
32+
static unsigned char askedToAccessCamera = false;
3233

3334
static void
3435
askToAccessCamera()
@@ -92,12 +93,18 @@ @interface SqueakVideoGrabber : NSObject <AVCaptureVideoDataOutputSampleBufferDe
9293
AVCaptureSession *captureSession;
9394
dispatch_queue_t queue;
9495
unsigned int *pixels;
96+
int pixelsByteSize;
97+
void *bufferAOrNil;
98+
void *bufferBOrNil;
99+
sqInt bufferSize;
95100
sqInt frameCount;
96101
int deviceID;
97102
int width;
98103
int height;
99104
int semaphoreIndex;
100-
bool bInitCalled;
105+
unsigned char errorCode;
106+
unsigned char bInitCalled;
107+
unsigned char useBNotA;
101108
}
102109
@end
103110

@@ -120,29 +127,49 @@ - (void)captureOutput:(AVCaptureOutput *)captureOutput
120127
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
121128
int currentWidth = CVPixelBufferGetWidth(imageBuffer);
122129
int currentHeight = CVPixelBufferGetHeight(imageBuffer);
123-
124-
if (!pixels) {
125-
pixels = malloc(currentWidth * currentHeight * 4);
126-
width = currentWidth;
127-
height = currentHeight;
130+
void *theBuffer;
131+
int alreadyHaveErrorCondition = errorCode;
132+
133+
width = currentWidth;
134+
height = currentHeight;
135+
if (bufferAOrNil) {
136+
if (bufferBOrNil) {
137+
theBuffer = useBNotA
138+
? bufferBOrNil
139+
: bufferAOrNil;
140+
useBNotA = !useBNotA;
141+
}
142+
else
143+
theBuffer = bufferAOrNil;
144+
if (currentWidth * currentHeight * 4 > bufferSize)
145+
errorCode = PrimErrWritePastObject;
128146
}
129-
else if (currentWidth != width
130-
|| currentHeight != height) {
131-
if (currentWidth * currentHeight > width * height) {
132-
if (pixels)
133-
free(pixels);
134-
pixels = malloc(currentWidth * currentHeight * 4);
147+
else {
148+
// No synchronization with CameraSetFrameBuffers et al.
149+
// We assume this is in a higher-priority thread than Smalltalk.
150+
if (pixels
151+
&& currentWidth * currentHeight * 4 > pixelsByteSize) {
152+
free(pixels);
153+
pixels = 0;
154+
}
155+
if (!pixels) {
156+
pixels = malloc(pixelsByteSize = currentWidth * currentHeight * 4);
157+
if (!pixels) {
158+
pixelsByteSize = 0;
159+
errorCode = PrimErrNoCMemory;
160+
}
135161
}
136-
width = currentWidth;
137-
height = currentHeight;
162+
theBuffer = pixels;
163+
}
164+
if (!errorCode) {
165+
CVPixelBufferLockBaseAddress(imageBuffer, 0);
166+
memcpy( theBuffer,
167+
CVPixelBufferGetBaseAddress(imageBuffer),
168+
currentWidth * currentHeight * 4);
169+
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
138170
}
139-
CVPixelBufferLockBaseAddress(imageBuffer, 0);
140-
memcpy( pixels,
141-
CVPixelBufferGetBaseAddress(imageBuffer),
142-
currentWidth * currentHeight * 4);
143-
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
144171
frameCount++;
145-
if (semaphoreIndex > 0)
172+
if (semaphoreIndex > 0 && !alreadyHaveErrorCondition)
146173
interpreterProxy->signalSemaphoreWithIndex(semaphoreIndex);
147174
}
148175

@@ -321,11 +348,6 @@ -(SqueakVideoGrabber*)initCapture:(int)deviceNum
321348
[captureSession startRunning];
322349

323350
bInitCalled = YES;
324-
if (pixels) {
325-
unsigned int *the_pixels = pixels;
326-
pixels = NULL;
327-
free(the_pixels);
328-
}
329351
frameCount = 0;
330352
semaphoreIndex = -1;
331353
grabbers[deviceID] = self;
@@ -347,14 +369,19 @@ -(void)stopCapture: (sqInt)cameraNum {
347369
for (AVCaptureOutput *output1 in captureSession.outputs)
348370
[captureSession removeOutput: output1];
349371
[captureSession stopRunning];
350-
unsigned int *the_pixels = pixels;
351-
pixels = NULL;
352-
free(the_pixels);
353-
frameCount = 0;
354-
bInitCalled = NO;
372+
// No need to free pixels carefully since camera is no longer running.
373+
if (pixels) {
374+
free(pixels);
375+
pixels = NULL;
376+
pixelsByteSize = 0;
377+
}
378+
frameCount = errorCode = 0;
379+
bInitCalled = useBNotA = NO;
355380
captureSession = NULL;
356381
captureOutput = NULL;
357382
captureInput = NULL;
383+
bufferAOrNil = bufferBOrNil = 0;
384+
bufferSize = 0;
358385
grabbers[cameraNum-1] = NULL;
359386
}
360387
}
@@ -451,16 +478,23 @@ -(void)stopCapture: (sqInt)cameraNum {
451478
sqInt
452479
CameraGetFrame(sqInt cameraNum, unsigned char *buf, sqInt pixelCount)
453480
{
454-
if (cameraNum<1 || cameraNum>CAMERA_COUNT)
481+
if (cameraNum < 1 || cameraNum > CAMERA_COUNT)
455482
return -1;
456483
SqueakVideoGrabber *grabber = grabbers[cameraNum-1];
457484
if (!grabber)
458485
return -1;
459-
if (grabber->pixels) {
486+
if (grabber->errorCode) {
487+
int theCode = grabber->errorCode;
488+
grabber->errorCode = 0;
489+
return -theCode;
490+
}
491+
if (grabber->pixels || grabber->bufferAOrNil) {
460492
int ourFrames = grabber->frameCount;
493+
if (grabber->pixels) {
461494
#define min(a,b) ((a)<=(b)?(a):(b))
462-
long actualPixelCount = grabber->width * grabber->height;
463-
memcpy(buf, grabber->pixels, min(pixelCount,actualPixelCount) * 4);
495+
long actualPixelCount = grabber->width * grabber->height;
496+
memcpy(buf, grabber->pixels, min(pixelCount,actualPixelCount) * 4);
497+
}
464498
grabber->frameCount = 0;
465499
return ourFrames;
466500
}
@@ -509,6 +543,43 @@ -(void)stopCapture: (sqInt)cameraNum {
509543
return PrimErrNotFound;
510544
}
511545

546+
// primSetCameraBuffers ensures buffers are pinned non-pointer objs if non-null
547+
sqInt
548+
CameraSetFrameBuffers(sqInt cameraNum, sqInt bufferA, sqInt bufferB)
549+
{
550+
SqueakVideoGrabber *grabber;
551+
552+
if (cameraNum < 1
553+
|| cameraNum > CAMERA_COUNT
554+
|| !(grabber = grabbers[cameraNum-1]))
555+
return PrimErrNotFound;
556+
557+
sqInt byteSize = interpreterProxy->byteSizeOf(bufferA);
558+
559+
if (bufferB
560+
&& byteSize != interpreterProxy->byteSizeOf(bufferB))
561+
return PrimErrInappropriate;
562+
563+
if (grabber->width * grabber->height * 4 > byteSize)
564+
return PrimErrWritePastObject;
565+
566+
grabber->bufferAOrNil = interpreterProxy->firstIndexableField(bufferA);
567+
grabber->bufferBOrNil = bufferB
568+
? interpreterProxy->firstIndexableField(bufferB)
569+
: (void *)0;
570+
grabber->bufferSize = byteSize;
571+
sqLowLevelMFence();
572+
// Need to free pixels carefully since camera may be running.
573+
if (grabber->pixels) {
574+
unsigned int *the_pixels = grabber->pixels;
575+
grabber->pixelsByteSize = 0;
576+
grabber->pixels = 0;
577+
sqLowLevelMFence();
578+
free(the_pixels);
579+
}
580+
return 0;
581+
}
582+
512583
sqInt
513584
CameraGetParam(sqInt cameraNum, sqInt paramNum)
514585
{

platforms/unix/plugins/CameraPlugin/sqCamera-linux.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,14 @@ CameraGetSemaphore(sqInt camNum)
936936
: 0;
937937
}
938938

939+
// primSetCameraBuffers ensures buffers are pinned non-pointer objs if non-null
940+
sqInt
941+
CameraSetFrameBuffers(sqInt cameraNum, sqInt bufferA, sqInt bufferB)
942+
{
943+
// For now
944+
return PrimErrUnsupported;
945+
}
946+
939947
/* Alas, see for example
940948
* https://www.kernel.org/doc/html/v5.4/media/uapi/v4l/async.html
941949
* "3.5. Asynchronous I/O This method is not defined yet."

platforms/win32/plugins/CameraPlugin/winCameraOps.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,14 @@ CameraGetSemaphore(sqInt cameraNum)
272272
: 0;
273273
}
274274

275+
// primSetCameraBuffers ensures buffers are pinned non-pointer objs if non-null
276+
sqInt
277+
CameraSetFrameBuffers(sqInt cameraNum, sqInt bufferA, sqInt bufferB)
278+
{
279+
// For now
280+
return PrimErrUnsupported;
281+
}
282+
275283
sqInt
276284
CameraSetSemaphore(sqInt cameraNum, sqInt semaphoreIndex)
277285
{

0 commit comments

Comments
 (0)