1111 */
1212
1313#include " sqVirtualMachine.h"
14+ #include " sqMemoryFence.h"
1415#include " CameraPlugin.h"
1516extern struct VirtualMachine *interpreterProxy;
1617
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
3334static void
3435askToAccessCamera ()
@@ -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 {
451478sqInt
452479CameraGetFrame (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+
512583sqInt
513584CameraGetParam (sqInt cameraNum, sqInt paramNum)
514585{
0 commit comments