From d51c1ac377067d3e469c111a15d85d4aaa187393 Mon Sep 17 00:00:00 2001 From: Eliot Miranda Date: Mon, 13 Feb 2023 16:53:35 -0800 Subject: [PATCH] CogVM source as per VMMaker.oscog-eem.3306 Add support for mirroring a frame in the CameraPlugin. Unimplemented on unix. --- .../Cross/plugins/CameraPlugin/CameraPlugin.h | 6 + .../CameraPlugin/AVFoundationVideoGrabber.m | 49 +++- .../plugins/CameraPlugin/sqCamera-linux.c | 36 ++- .../plugins/CameraPlugin/winCameraOps.cpp | 222 +++++++++--------- src/plugins/CameraPlugin/CameraPlugin.c | 86 ++++++- 5 files changed, 265 insertions(+), 134 deletions(-) diff --git a/platforms/Cross/plugins/CameraPlugin/CameraPlugin.h b/platforms/Cross/plugins/CameraPlugin/CameraPlugin.h index c40e3a08f4..799ee76c45 100644 --- a/platforms/Cross/plugins/CameraPlugin/CameraPlugin.h +++ b/platforms/Cross/plugins/CameraPlugin/CameraPlugin.h @@ -9,7 +9,13 @@ char *CameraName(sqInt cameraNum); char *CameraUID(sqInt cameraNum); sqInt CameraExtent(sqInt cameraNum); sqInt CameraGetFrame(sqInt cameraNum, unsigned char *buf, sqInt pixelCount); + +// CameraGet/SetParam names +#define FrameCount 1 +#define FrameByteSize 2 +#define MirrorImage 3 sqInt CameraGetParam(sqInt cameraNum, sqInt paramNum); +sqInt CameraSetParam(sqInt cameraNum, sqInt paramNum, sqInt paramValue); sqInt CameraGetSemaphore(sqInt cameraNum); sqInt CameraSetSemaphore(sqInt cameraNum, sqInt semaphoreIndex); sqInt CameraSetFrameBuffers(sqInt cameraNum, sqInt bufferA, sqInt bufferBorNil); diff --git a/platforms/iOS/plugins/CameraPlugin/AVFoundationVideoGrabber.m b/platforms/iOS/plugins/CameraPlugin/AVFoundationVideoGrabber.m index 6b33afea5e..233953c374 100644 --- a/platforms/iOS/plugins/CameraPlugin/AVFoundationVideoGrabber.m +++ b/platforms/iOS/plugins/CameraPlugin/AVFoundationVideoGrabber.m @@ -110,6 +110,7 @@ @interface SqueakVideoGrabber : NSObject = 0;) + *mirrored++ = *--pixelp; + } + } + else + memcpy( theBuffer, + CVPixelBufferGetBaseAddress(imageBuffer), + currentWidth * currentHeight * 4); CVPixelBufferUnlockBaseAddress(imageBuffer, 0); if (theBuffer != pixels) freshestBuffer = theBuffer; @@ -355,6 +366,7 @@ -(SqueakVideoGrabber*)initCapture:(int)deviceNum bInitCalled = YES; frameCount = 0; semaphoreIndex = -1; + mirrorImage = 0; grabbers[deviceID] = self; return self; } @@ -612,12 +624,33 @@ -(void)stopCapture: (sqInt)cameraNum { sqInt CameraGetParam(sqInt cameraNum, sqInt paramNum) { - if (!CameraIsOpen(cameraNum)) return -1; - if (paramNum == 1) return grabbers[cameraNum-1]->frameCount; - if (paramNum == 2) return grabbers[cameraNum-1]->width - * grabbers[cameraNum-1]->height * 4; + if (!CameraIsOpen(cameraNum)) + return -PrimErrNotFound; + + SqueakVideoGrabber *grabber = grabbers[cameraNum-1]; + + switch (paramNum) { + case FrameCount: return grabber->frameCount; + case FrameByteSize: return grabber->width * grabber->height * 4; + case MirrorImage: return grabber->mirrorImage; + } + return -PrimErrBadArgument; +} + +sqInt +CameraSetParam(sqInt cameraNum, sqInt paramNum, sqInt paramValue) +{ + if (!CameraIsOpen(cameraNum)) + return -PrimErrNotFound; + + SqueakVideoGrabber *grabber = grabbers[cameraNum-1]; - return -2; + if (paramNum == MirrorImage) { + sqInt oldValue = grabber->mirrorImage; + grabber->mirrorImage = paramValue; + return oldValue; + } + return -PrimErrBadArgument; } sqInt diff --git a/platforms/unix/plugins/CameraPlugin/sqCamera-linux.c b/platforms/unix/plugins/CameraPlugin/sqCamera-linux.c index a61401fa8f..c945554574 100644 --- a/platforms/unix/plugins/CameraPlugin/sqCamera-linux.c +++ b/platforms/unix/plugins/CameraPlugin/sqCamera-linux.c @@ -117,6 +117,7 @@ struct camInfo_t { unsigned long frameCount; struct v4l2_capability cap; + unsigned char mirrorImage; } camInfo[CAMERA_COUNT]; typedef struct camInfo_t *camPtr; @@ -202,6 +203,7 @@ libCon(void) cam->ioMethod = IO_METHOD_MMAP; cam->nBuffers = 2; cam->frameCount = 0; + cam->mirrorImage = 0; cam->semaphoreIndex = -1; vBufReset(&(cam->vBuf)); /* Pixel format auto selected for ease/speed of conversion */ @@ -774,19 +776,35 @@ initCamera(camPtr cam, int w, int h) sqInt -CameraGetParam(sqInt camNum, sqInt paramNum) +CameraGetParam(sqInt cameraNum, sqInt paramNum) { camPtr cam = camera(camNum); - if (!cam) return -1; - if (paramNum == 1) - return cam->frameCount - ? cam->frameCount - : (cameraReadable(cam) ? 1 : 0); - if (paramNum == 2) - return cam->bmWidth * cam->bmHeight * 4; + if (!cam) + return -PrimErrNotFound; + switch (paramNum) { + case FrameCount: return cam->frameCount; + case FrameByteSize: return cam->width * cam->height * 4; + case MirrorImage: return cam->mirrorImage; + } + return -PrimErrBadArgument; +} - return -2; +sqInt +CameraSetParam(sqInt cameraNum, sqInt paramNum, sqInt paramValue) +{ + camPtr cam = camera(camNum); + + if (!cam) + return -PrimErrNotFound; + if (paramNum == MirrorImage) { + sqInt oldValue = cam->mirrorImage; + if (1) // For now + return PrimErrUnsupported; + cam->mirrorImage = paramValue; + return oldValue; + } + return -PrimErrBadArgument; } diff --git a/platforms/win32/plugins/CameraPlugin/winCameraOps.cpp b/platforms/win32/plugins/CameraPlugin/winCameraOps.cpp index db24dc30b2..eb56342eae 100644 --- a/platforms/win32/plugins/CameraPlugin/winCameraOps.cpp +++ b/platforms/win32/plugins/CameraPlugin/winCameraOps.cpp @@ -26,18 +26,25 @@ extern "C" { extern struct VirtualMachine *interpreterProxy; -static inline void * -forwardDeclarationHack (struct Camera *aCamera, long lThisBufferSize, - int *widthp, int *heightp, int *semaphoreIndexp, - char *errorCodep, char *alreadyErrorp); +typedef struct OurCamera { + void * bufferAOrNil; + void * bufferBOrNil; + void * freshestBuffer; + int bufferSize; + int width; + int height; + int semaphoreIndex; + char useBNotA; + char errorCode; + char mirrorImage; +} OurCamera; + +static inline OurCamera * +forwardDeclarationHack (struct Camera *aCamera); static inline void setErrorCode(struct Camera *aCamera, char errorCode); - -static unsigned int offsetOfFreshestBuffer = 0; } -#define FLIP_IN_CAPTURE 1 - class CSampleGrabberCB : public ISampleGrabberCB { public: struct Camera *myCamera; @@ -72,22 +79,30 @@ class CSampleGrabberCB : public ISampleGrabberCB { STDMETHODIMP BufferCB(double dblThisSampleTime, BYTE * pThisBuf, long lThisBufSize) { - void *theBuffer; - char alreadyHaveErrorCondition = 0, errorCode = 0; - int semaphoreIndex; - int width, height; + void * theBuffer; int pixelBytes = (lThisBufSize / 3) * 4; if (!pThisBuf || !lThisBufSize) return E_POINTER; - theBuffer = forwardDeclarationHack (myCamera, - pixelBytes, - &width, &height, - &semaphoreIndex, - &errorCode, - &alreadyHaveErrorCondition); - if (!theBuffer && !errorCode) { + OurCamera *aCamera = forwardDeclarationHack(myCamera); + int alreadyHaveErrorCondition = aCamera->errorCode; + + if (aCamera->bufferBOrNil) { + theBuffer = aCamera->useBNotA + ? aCamera->bufferBOrNil + : aCamera->bufferAOrNil; + aCamera->useBNotA = !aCamera->useBNotA; + } + else + theBuffer = aCamera->bufferAOrNil; + + if (lThisBufSize > aCamera->bufferSize) { + aCamera->errorCode = PrimErrWritePastObject; + theBuffer = 0; + } + + if (!theBuffer && !aCamera->errorCode) { // No synchronization with CameraSetFrameBuffers et al. // We assume this is in a higher-priority thread than Smalltalk. if (pFrameBuf @@ -106,34 +121,49 @@ class CSampleGrabberCB : public ISampleGrabberCB { theBuffer = pFrameBuf; } - // Copy the bitmap data into the chosen buffer - if (!errorCode && theBuffer) { -#if FLIP_IN_CAPTURE - // flip image vertically while copying to buffer - // N.B. For certain cameras this is unnecessary, see - // https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/bee4f7c0-5938-43ce-9db5-59a60b5f80bb/flip-webcam-vertically?forum=windowsdirectshowdevelopment - unsigned char *pSrc = pThisBuf; - for (int y = height - 1; y >= 0; y--) { - unsigned char *pDst = (unsigned char *)theBuffer + (4 * y * width); - int x = 0; - while (++x <= width) { - pDst[0] = pSrc[0]; // red - pDst[1] = pSrc[1]; // green - pDst[2] = pSrc[2]; // blue - pDst[3] = 255; // alpha - pDst += 4; - pSrc += 3; +// Copy the bitmap data into the chosen buffer +// flip image vertically while copying to buffer +// mirror image horizontally if aCamera->mirrorImage +// N.B. For certain cameras this is unnecessary, see +// https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/bee4f7c0-5938-43ce-9db5-59a60b5f80bb/flip-webcam-vertically?forum=windowsdirectshowdevelopment + + if (!aCamera->errorCode && theBuffer) { + int x, y, width = aCamera->width; + unsigned char *pDst, *pSrc = pThisBuf; + if (aCamera->mirrorImage) { + for (y = aCamera->height; y > 0; --y) { + pDst = (unsigned char *)theBuffer + (4 * y * width); + x = width; + while (--x >= 0) { + pDst -= 4; + pDst[0] = pSrc[0]; // red + pDst[1] = pSrc[1]; // green + pDst[2] = pSrc[2]; // blue + pDst[3] = 255; // alpha + pSrc += 3; + } + } + } + else { + for (y = aCamera->height - 1; y >= 0; --y) { + pDst = (unsigned char *)theBuffer + (4 * y * width); + x = width; + while (--x >= 0) { + pDst[0] = pSrc[0]; // red + pDst[1] = pSrc[1]; // green + pDst[2] = pSrc[2]; // blue + pDst[3] = 255; // alpha + pDst += 4; + pSrc += 3; + } } } -#else - memcpy(theBuffer, pThisBuf, lThisBufSize); -#endif if (theBuffer != pFrameBuf) - *(void **)((char *)myCamera + offsetOfFreshestBuffer) = theBuffer; + aCamera->freshestBuffer = theBuffer; } ++frameCount; - if (!alreadyHaveErrorCondition && semaphoreIndex > 0) - interpreterProxy->signalSemaphoreWithIndex(semaphoreIndex); + if (!alreadyHaveErrorCondition && aCamera->semaphoreIndex > 0) + interpreterProxy->signalSemaphoreWithIndex(aCamera->semaphoreIndex); return 0; } }; @@ -158,12 +188,13 @@ typedef struct Camera { void * bufferAOrNil; void * bufferBOrNil; void * freshestBuffer; - sqInt bufferSize; + int bufferSize; int width; int height; int semaphoreIndex; char useBNotA; char errorCode; + char mirrorImage; } Camera; #define CAMERA_COUNT 4 @@ -193,39 +224,12 @@ static void SetFramesPerSecond(int fps); #endif #define SAFE_RELEASE(x) { if ((x) && InRangePtr(x)) (x)->Release(); (x) = nullptr; } -/* Answer the buffer to use if the camera has a pinned object frame buffer, - * or nil. Load other usefu variables. This is a hack to get around Camera - * and CSampleGrabberCB needing to know about each other. - */ -static inline void * -forwardDeclarationHack (struct Camera *aCamera, long lThisBufferSize, - int *widthp, int *heightp, int *semaphoreIndexp, - char *errorCodep, char *alreadyErrorp) -{ - void *theBuffer; - - if (aCamera->errorCode) - *alreadyErrorp = 1; - *widthp = aCamera->width; - *heightp = aCamera->height; - *semaphoreIndexp = aCamera->semaphoreIndex; - - if (!aCamera->bufferAOrNil) - return 0; +// Hack around Camera and CSampleGrabberCB needing to know about each other... - if (aCamera->bufferBOrNil) { - theBuffer = aCamera->useBNotA - ? aCamera->bufferBOrNil - : aCamera->bufferAOrNil; - aCamera->useBNotA = !aCamera->useBNotA; - } - else - theBuffer = aCamera->bufferAOrNil; - if (lThisBufferSize > aCamera->bufferSize) { - *errorCodep = aCamera->errorCode = PrimErrWritePastObject; - return 0; - } - return theBuffer; +static inline OurCamera * +forwardDeclarationHack(struct Camera *aCamera) +{ + return (OurCamera *)&(aCamera->bufferAOrNil); } static inline void @@ -306,31 +310,10 @@ CameraGetFrame(sqInt cameraNum, unsigned char *buf, sqInt pixelCount) if (camera->bufferAOrNil) return framesSinceLastCall; -#if FLIP_IN_CAPTURE if (pixelCount > (camera->mCB.lFrameBufSize / 4)) pixelCount = camera->mCB.lFrameBufSize / 4; memcpy(buf, camera->mCB.pFrameBuf, pixelCount * 4); -#else - if (pixelCount > (camera->mCB.lFrameBufSize / 3)) - pixelCount = camera->mCB.lFrameBufSize / 3; - - // flip image vertically while copying to Squeak buf - // N.B. For certain cameras this is unnecessary, see - // https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/bee4f7c0-5938-43ce-9db5-59a60b5f80bb/flip-webcam-vertically?forum=windowsdirectshowdevelopment - unsigned char *pSrc = camera->mCB.pFrameBuf; - for (int w = camera->width, y = camera->height - 1; y >= 0; y--) { - unsigned char *pDst = &buf[4 * y * w]; - for (int x = 0; x < w; x++) { - pDst[0] = pSrc[0]; // red - pDst[1] = pSrc[1]; // green - pDst[2] = pSrc[2]; // blue - pDst[3] = 255; // alpha - pDst += 4; - pSrc += 3; - } - } -#endif return framesSinceLastCall; } @@ -454,21 +437,44 @@ CameraGetParam(sqInt cameraNum, sqInt paramNum) // for debugging and testing Camera *camera; if (!(camera = ActiveCamera(cameraNum))) - return -1; - if (paramNum == 1) - return camera->mCB.frameCount; - if (paramNum == 2) - return camera->mCB.lFrameBufSize; + return -PrimErrNotFound; + + // sanity check the forward declaration hack + if (&forwardDeclarationHack(camera)->bufferAOrNil != &camera->bufferAOrNil + || &forwardDeclarationHack(camera)->mirrorImage != &camera->mirrorImage) + return -PrimErrInternalError; + + switch (paramNum) { + case FrameCount: return camera->mCB.frameCount; + case FrameByteSize: return camera->mCB.lFrameBufSize; + case MirrorImage: return camera->mirrorImage; #if 0 - /* Negative values are platform specific info */ - if (paramNum == -2) { + /* Negative values are platform specific info */ + case -2: { long capsFlags = 0; camera->pVideoControl->GetCaps(camera->pVideoControl, &capsFlags); return capsFlags; - } + } #endif + } - return -2; + return -PrimErrBadArgument; +} + +sqInt +CameraSetParam(sqInt cameraNum, sqInt paramNum, sqInt paramValue) +{ + Camera *camera; + + if (!(camera = ActiveCamera(cameraNum))) + return -PrimErrNotFound; + + if (paramNum == MirrorImage) { + sqInt oldValue = camera->mirrorImage; + camera->mirrorImage = paramValue; + return oldValue; + } + return -PrimErrBadArgument; } ////////////////////////////////////////////// @@ -817,11 +823,7 @@ SetClosestWidthAndFrameRate(IAMStreamConfig *pCameraStream, int desiredWidth) } sqInt -cameraInit(void) -{ - offsetOfFreshestBuffer = (unsigned int)&((Camera *)0)->freshestBuffer; - return 1; -} +cameraInit(void) { return 1; } sqInt cameraShutdown(void) diff --git a/src/plugins/CameraPlugin/CameraPlugin.c b/src/plugins/CameraPlugin/CameraPlugin.c index d4b43dddf9..fdc7fd1d19 100644 --- a/src/plugins/CameraPlugin/CameraPlugin.c +++ b/src/plugins/CameraPlugin/CameraPlugin.c @@ -1,9 +1,9 @@ /* Automatically generated by - VMPluginCodeGenerator VMMaker.oscog-eem.3305 uuid: 087be167-79b2-4b97-b9a5-5e78e6d21f33 + VMPluginCodeGenerator VMMaker.oscog-eem.3306 uuid: 27c1af08-8727-4513-81b1-28bcf6486cf1 from - CameraPlugin VMMaker.oscog-eem.3305 uuid: 087be167-79b2-4b97-b9a5-5e78e6d21f33 + CameraPlugin VMMaker.oscog-eem.3306 uuid: 27c1af08-8727-4513-81b1-28bcf6486cf1 */ -static char __buildInfo[] = "CameraPlugin VMMaker.oscog-eem.3305 uuid: 087be167-79b2-4b97-b9a5-5e78e6d21f33 " __DATE__ ; +static char __buildInfo[] = "CameraPlugin VMMaker.oscog-eem.3306 uuid: 27c1af08-8727-4513-81b1-28bcf6486cf1 " __DATE__ ; #include "config.h" @@ -34,6 +34,10 @@ static char __buildInfo[] = "CameraPlugin VMMaker.oscog-eem.3305 uuid: 087be167- #endif +/*** Constants ***/ +#define MirrorImage 3 + + /*** Function Prototypes ***/ EXPORT(const char*) getModuleName(void); EXPORT(sqInt) initialiseModule(void); @@ -49,6 +53,7 @@ EXPORT(sqInt) primGetParam(void); EXPORT(sqInt) primOpenCamera(void); EXPORT(sqInt) primSetCameraBuffers(void); EXPORT(sqInt) primSetCameraSemaphore(void); +EXPORT(sqInt) primSetParam(void); EXPORT(sqInt) setInterpreter(struct VirtualMachine *anInterpreter); EXPORT(sqInt) shutdownModule(void); @@ -56,6 +61,7 @@ EXPORT(sqInt) shutdownModule(void); /*** Variables ***/ #if !defined(SQUEAK_BUILTIN_PLUGIN) +static sqInt (*booleanValueOf)(sqInt obj); static sqInt (*failed)(void); static void * (*firstIndexableField)(sqInt oop); #if !defined(integerValueOf) @@ -68,6 +74,7 @@ static sqInt (*isPinned)(sqInt anObject); static sqInt (*isWords)(sqInt oop); static sqInt (*isWordsOrBytes)(sqInt oop); static sqInt (*methodArgumentCount)(void); +static sqInt (*methodReturnBool)(sqInt boolean); static sqInt (*methodReturnInteger)(sqInt integer); static sqInt (*methodReturnReceiver)(void); static sqInt (*methodReturnString)(char *aCString); @@ -80,6 +87,7 @@ static sqInt (*stackIntegerValue)(sqInt offset); static sqInt (*stackValue)(sqInt offset); static sqInt (*success)(sqInt aBoolean); #else /* !defined(SQUEAK_BUILTIN_PLUGIN) */ +extern sqInt booleanValueOf(sqInt obj); extern sqInt failed(void); extern void * firstIndexableField(sqInt oop); #if !defined(integerValueOf) @@ -96,6 +104,7 @@ extern sqInt isPinned(sqInt anObject); extern sqInt isWords(sqInt oop); extern sqInt isWordsOrBytes(sqInt oop); extern sqInt methodArgumentCount(void); +extern sqInt methodReturnBool(sqInt boolean); extern sqInt methodReturnInteger(sqInt integer); extern sqInt methodReturnReceiver(void); extern sqInt methodReturnString(char *aCString); @@ -110,7 +119,7 @@ extern sqInt success(sqInt aBoolean); extern #endif struct VirtualMachine* interpreterProxy; -static const char *moduleName = "CameraPlugin VMMaker.oscog-eem.3305 " INT_EXT; +static const char *moduleName = "CameraPlugin VMMaker.oscog-eem.3306 " INT_EXT; @@ -322,7 +331,9 @@ primGetLatestBufferIndex(void) } -/* Answer the given integer parameter of the given camera. */ +/* Answer the requested parameter value of the given camera. + See platforms/Cross/plugins/CameraPlugin/CameraPlugin.h for the list of + parameter names. */ /* CameraPlugin>>#primGetParam */ EXPORT(sqInt) @@ -330,11 +341,26 @@ primGetParam(void) { sqInt cameraNum; sqInt paramNum; + sqInt result; cameraNum = stackIntegerValue(1); paramNum = stackIntegerValue(0); - if (!(failed())) { - methodReturnInteger(CameraGetParam(cameraNum, paramNum)); + if (failed()) { + primitiveFailFor(PrimErrBadArgument); + } + else { + result = CameraGetParam(cameraNum, paramNum); + if (result < 0) { + primitiveFailFor(-result); + } + else { + if (paramNum == MirrorImage) { + methodReturnBool(result); + } + else { + methodReturnInteger(result); + } + } } return 0; } @@ -453,6 +479,48 @@ primSetCameraSemaphore(void) } +/* Set the requested parameter value of the given camera, and answer its + previous value. + See platforms/Cross/plugins/CameraPlugin/CameraPlugin.h for the list of + parameter names. + */ + + /* CameraPlugin>>#primSetParam */ +EXPORT(sqInt) +primSetParam(void) +{ + sqInt cameraNum; + sqInt paramNum; + sqInt paramValue; + sqInt result; + + cameraNum = stackIntegerValue(2); + paramNum = stackIntegerValue(1); + paramValue = stackValue(0); + paramValue = (isIntegerObject(paramValue) + ? integerValueOf(paramValue) + : booleanValueOf(paramValue)); + if (failed()) { + primitiveFailFor(PrimErrBadArgument); + } + else { + result = CameraSetParam(cameraNum, paramNum, paramValue); + if (result < 0) { + primitiveFailFor(-result); + } + else { + if (paramNum == MirrorImage) { + methodReturnBool(result); + } + else { + methodReturnInteger(result); + } + } + } + return 0; +} + + /* Note: This is coded so that it can be run in Squeak. */ /* InterpreterPlugin>>#setInterpreter: */ @@ -470,6 +538,7 @@ setInterpreter(struct VirtualMachine *anInterpreter) if (ok) { #if !defined(SQUEAK_BUILTIN_PLUGIN) + booleanValueOf = interpreterProxy->booleanValueOf; failed = interpreterProxy->failed; firstIndexableField = interpreterProxy->firstIndexableField; #if !defined(integerValueOf) @@ -488,6 +557,7 @@ setInterpreter(struct VirtualMachine *anInterpreter) isWords = interpreterProxy->isWords; isWordsOrBytes = interpreterProxy->isWordsOrBytes; methodArgumentCount = interpreterProxy->methodArgumentCount; + methodReturnBool = interpreterProxy->methodReturnBool; methodReturnInteger = interpreterProxy->methodReturnInteger; methodReturnReceiver = interpreterProxy->methodReturnReceiver; methodReturnString = interpreterProxy->methodReturnString; @@ -530,6 +600,7 @@ void* CameraPlugin_exports[][3] = { {(void*)_m, "primOpenCamera\000\000\000", (void*)primOpenCamera}, {(void*)_m, "primSetCameraBuffers\000\000\000", (void*)primSetCameraBuffers}, {(void*)_m, "primSetCameraSemaphore\000\000\000", (void*)primSetCameraSemaphore}, + {(void*)_m, "primSetParam\000\000\000", (void*)primSetParam}, {(void*)_m, "setInterpreter", (void*)setInterpreter}, {(void*)_m, "shutdownModule", (void*)shutdownModule}, {NULL, NULL, NULL} @@ -550,6 +621,7 @@ EXPORT(signed short) primGetParamMetadata = 0; EXPORT(signed short) primOpenCameraMetadata = 0; EXPORT(signed short) primSetCameraBuffersMetadata = 0; EXPORT(signed short) primSetCameraSemaphoreMetadata = 0; +EXPORT(signed short) primSetParamMetadata = 0; #endif // SPURVM #endif // ifdef SQ_BUILTIN_PLUGIN