Skip to main content

Post-processing tools 2024 R1

shared_memory_image_client_python

Last update: 16.07.2025
Go to the documentation of this file.
1/**************************************************************************
2 *
3 * ****************************************
4 * Copyright 2020 ANSYS Inc.
5 * All Rights Reserved.
6 *
7 * Restricted Rights Legend
8 *
9 * Government is subject to restrictions as
10 * set forth in subdivision [(b)(3)(ii)] of
11 * the Rights in Technical Data and Computer
12 * Software clause at 52.227-7013.
13 * ****************************************
14 *
15 *************************************************************************/
16
17/* Warning this is a HACK to get around Python issues mixing debug modules with
18 * release Python builds. */
19
20#ifdef _DEBUG
21#undef _DEBUG
22#include <Python.h>
23#define _DEBUG
24#else
25#include <Python.h>
26#endif
27
29
30#define MAX_STREAMS 50
31static SharedMemoryImageStream streamlist[MAX_STREAMS] = { 0 };
80#ifdef DOXYGEN
95stream_id stream_create(str filename, int options=IMAGESTREAM_OPTIONS_NONE);
96
107frame stream_lock(stream_id id);
108
120
130
145SharedMemoryImageError stream_putframe(stream_id id, int width, int height, int framenumber, bytes pixeldata);
146
147#endif
148
149/* Utility functions PyDict_SetItemString with DECREF */
150static int PyDict_SetItemString_DECREF(PyObject *dict, const char *key, PyObject *val)
151{
152 int ret = PyDict_SetItemString(dict, key, val);
153 if (ret == 0) Py_DECREF(val);
154 return(ret);
155}
156
157// Given an error code, return the code or raise an exception
158static PyObject *return_error(int err)
159{
160 if (err >= IMAGESTREAM_NOERROR) {
161 return PyLong_FromLong(err);
162 }
163 PyErr_SetString(PyExc_RuntimeError, SharedMemoryImageStream_errorstring(err));
164 return NULL;
165}
166
167static PyObject *Py_stream_create(PyObject *self, PyObject *args, PyObject *keywds)
168{
169 char *filename = NULL;
170 uint32_t options = 0;
171 static const char *kwlist[] = { "filename", "options", NULL };
172 if (!PyArg_ParseTupleAndKeywords(args, keywds, (char *)"s|i", (char **)kwlist,
173 &filename, &options)) return NULL;
174 int32_t stream_id = -1;
175 for (int i = 0; i < MAX_STREAMS; i++) {
176 if (!streamlist[i]) {
177 stream_id = i;
178 }
179 }
180 if (stream_id == -1) {
181 PyErr_SetString(PyExc_RuntimeError, "Maximum number of simultaneous streams open");
182 return NULL;
183 }
185 SharedMemoryImageError err = SharedMemoryImageStream_create(filename, options, &stream);
186 if (err != IMAGESTREAM_NOERROR) {
187 return return_error(err);
188 }
189 streamlist[stream_id] = stream;
190 return PyLong_FromLong(stream_id);
191}
192
193static PyObject *Py_stream_lock(PyObject *self, PyObject *args, PyObject *keywds)
194{
195 int32_t stream_id = -1;
196 static const char *kwlist[] = { "stream_id", NULL };
197 if (!PyArg_ParseTupleAndKeywords(args, keywds, (char *)"i", (char **)kwlist,
198 &stream_id)) return NULL;
199 if ((stream_id < 0) || (stream_id >= MAX_STREAMS) || !streamlist[stream_id]) {
200 PyErr_SetString(PyExc_RuntimeError, "Unknown stream id");
201 return NULL;
202 }
203 SharedMemoryFrame frame;
204 SharedMemoryImageError err = SharedMemoryImageStream_lock(streamlist[stream_id], &frame);
205 if (err != IMAGESTREAM_NOERROR) {
206 return return_error(err);
207 }
208 PyObject *ret = PyDict_New();
209 PyDict_SetItemString_DECREF(ret, "width", PyLong_FromLong(frame->iWidth));
210 PyDict_SetItemString_DECREF(ret, "height", PyLong_FromLong(frame->iHeight));
211 PyDict_SetItemString_DECREF(ret, "framenumber", PyLong_FromLong(frame->iFrame));
212 PyDict_SetItemString_DECREF(ret, "pixeldata", PyBytes_FromStringAndSize(frame->buffer,
213 frame->iWidth*3*frame->iHeight));
214 return ret;
215}
216
217static PyObject *Py_stream_unlock(PyObject *self, PyObject *args, PyObject *keywds)
218{
219 int32_t stream_id = -1;
220 static const char *kwlist[] = { "stream_id", NULL };
221 if (!PyArg_ParseTupleAndKeywords(args, keywds, (char *)"i", (char **)kwlist,
222 &stream_id)) return NULL;
223 if ((stream_id < 0) || (stream_id >= MAX_STREAMS) || !streamlist[stream_id]) {
224 PyErr_SetString(PyExc_RuntimeError, "Unknown stream id");
225 return NULL;
226 }
227 SharedMemoryImageError err = SharedMemoryImageStream_unlock(streamlist[stream_id]);
228 return return_error(err);
229}
230
231static PyObject *Py_stream_putframe(PyObject *self, PyObject *args, PyObject *keywds)
232{
233 int32_t stream_id = -1;
234 int32_t iWidth = 0;
235 int32_t iHeight = 0;
236 uint32_t iFrame = 0;
237 PyObject *pData = NULL;
238 static const char *kwlist[] = { "stream_id", "width", "height", "framenumber", "pixeldata", NULL };
239 if (!PyArg_ParseTupleAndKeywords(args, keywds, (char *)"iiiiO", (char **)kwlist,
240 &stream_id, &iWidth, &iHeight, &iFrame, &pData)) return NULL;
241 if ((stream_id < 0) || (stream_id >= MAX_STREAMS) || !streamlist[stream_id]) {
242 PyErr_SetString(PyExc_RuntimeError, "Unknown stream id");
243 return NULL;
244 }
245 int32_t frame_size = iWidth * iHeight * 3;
246 if (frame_size < 1) {
247 PyErr_SetString(PyExc_RuntimeError, "Invalid frame size");
248 return NULL;
249 }
250 if (!PyBytes_Check(pData)) {
251 PyErr_SetString(PyExc_RuntimeError, "Pixeldata must be a bytes object");
252 return NULL;
253 }
254 Py_ssize_t data_size = PyBytes_Size(pData);
255 if (data_size < frame_size) {
256 PyErr_SetString(PyExc_RuntimeError, "Pixeldata is too small for specified frame size");
257 return NULL;
258 }
259 struct _SharedMemoryFrame frame;
260 frame.iFrame = iFrame;
261 frame.iWidth = iWidth;
262 frame.iHeight = iHeight;
263 frame.buffer = PyBytes_AsString(pData);
264 SharedMemoryImageError err = SharedMemoryImageStream_putframe(streamlist[stream_id], &frame);
265 return return_error(err);
266}
267
268static PyObject *Py_stream_destroy(PyObject *self, PyObject *args, PyObject *keywds)
269{
270 int32_t stream_id = -1;
271 static const char *kwlist[] = { "stream_id", NULL };
272 if (!PyArg_ParseTupleAndKeywords(args, keywds, (char *)"i", (char **)kwlist,
273 &stream_id)) return NULL;
274 if ((stream_id < 0) || (stream_id >= MAX_STREAMS) || !streamlist[stream_id]) {
275 PyErr_SetString(PyExc_RuntimeError, "Unknown stream id");
276 return NULL;
277 }
278 SharedMemoryImageError err = SharedMemoryImageStream_destroy(streamlist[stream_id]);
279 streamlist[stream_id] = NULL;
280 return return_error(err);
281}
282
283static void stream_python_shutdown(void)
284{
285 for (int i = 0; i < MAX_STREAMS; i++) {
286 if (streamlist[i]) SharedMemoryImageStream_destroy(streamlist[i]);
287 streamlist[i] = NULL;
288 }
289}
290
292
293/* Module definition */
294static PyObject *pmodule = NULL;
295static PyMethodDef py_methods[] = {
296 {(char *)"stream_create", (PyCFunction)Py_stream_create, METH_VARARGS | METH_KEYWORDS,
297 (char *)"Create a new shared memory image stream"},
298 {(char *)"stream_lock", (PyCFunction)Py_stream_lock, METH_VARARGS | METH_KEYWORDS,
299 (char *)"Lock and return the next image from an image stream"},
300 {(char *)"stream_unlock", (PyCFunction)Py_stream_unlock, METH_VARARGS | METH_KEYWORDS,
301 (char *)"Release the lock on an image stream"},
302 {(char *)"stream_putframe", (PyCFunction)Py_stream_putframe, METH_VARARGS | METH_KEYWORDS,
303 (char *)"Push a new frame into the image stream"},
304 {(char *)"stream_destroy", (PyCFunction)Py_stream_destroy, METH_VARARGS | METH_KEYWORDS,
305 (char *)"Destroy an image stream"},
306 {NULL, NULL, 0, NULL}
307};
308
309#if PY_MAJOR_VERSION >= 3
310
311static struct PyModuleDef moduledef = {
312 PyModuleDef_HEAD_INIT,
313 "ensight_grpc_shmem",
314 "EnSight gRPC shared memory image stream",
315 -1,
316 py_methods,
317 NULL, /* m_reload */
318 NULL, /* m_traverse */
319 NULL, /* m_clear */
320 NULL, /* m_free */
321};
322
323PyMODINIT_FUNC PyInit_ensight_grpc_shmem()
324{
325 pmodule = PyModule_Create(&moduledef);
326#else
327PyMODINIT_FUNC initensight_grpc_shmem()
328{
329 pmodule = Py_InitModule3("ensight_grpc_shmem", py_methods, "EnSight gRPC shared memory image stream");
330#endif
331
332 // Common (2 & 3) code starts here -------------------------------------------------
333 PyModule_AddStringConstant(pmodule, "version", IMAGESTREAM_API_VERSION);
334
335 PyModule_AddIntConstant(pmodule, "OPTIONS_NONE", (int)IMAGESTREAM_OPTIONS_NONE);
336 PyModule_AddIntConstant(pmodule, "OPTIONS_SERVER", (int)IMAGESTREAM_OPTIONS_SERVER);
337 PyModule_AddIntConstant(pmodule, "OPTIONS_FLIPVERTICAL", (int)IMAGESTREAM_OPTIONS_FLIPVERTICAL);
338
339 PyModule_AddIntConstant(pmodule, "ERROR_NOERROR", (int)IMAGESTREAM_NOERROR);
340 PyModule_AddIntConstant(pmodule, "ERROR_NO_BUFFERAVAILABLE", (int)IMAGESTREAM_NO_BUFFERAVAILABLE);
341 PyModule_AddIntConstant(pmodule, "ERROR_NO_FRAMEAVAILABLE", (int)IMAGESTREAM_NO_FRAMEAVAILABLE);
342 PyModule_AddIntConstant(pmodule, "ERROR_RECONNECTING", (int)IMAGESTREAM_RECONNECTING);
343 PyModule_AddIntConstant(pmodule, "ERROR_UNCONNECTED", (int)IMAGESTREAM_UNCONNECTED);
344
345 /* cleanup on exit */
346 Py_AtExit(stream_python_shutdown);
347 // Common (2 & 3) code ends here -------------------------------------------------
348
349#if PY_MAJOR_VERSION >= 3
350 return pmodule;
351}
352#else
353 return;
354}
355#endif
356
SharedMemoryImageError SharedMemoryImageStream_create(const char *filename, uint32_t options, SharedMemoryImageStream *stream)
SharedMemoryImageStream_create create a stream client instance.
int SharedMemoryImageError
common error codes see the non-hex, non-string IMAGESTREAM macros
const char * SharedMemoryImageStream_errorstring(SharedMemoryImageError err)
SharedMemoryImageStream_errorstring convert an error code into a human string.
#define IMAGESTREAM_UNCONNECTED
SharedMemoryImageError SharedMemoryImageStream_destroy(SharedMemoryImageStream stream)
SharedMemoryImageStream_destroy destroy a stream interface, releasing its resources.
#define IMAGESTREAM_OPTIONS_FLIPVERTICAL
#define IMAGESTREAM_API_VERSION
#define IMAGESTREAM_NO_BUFFERAVAILABLE
#define IMAGESTREAM_NO_FRAMEAVAILABLE
#define IMAGESTREAM_OPTIONS_NONE
#define IMAGESTREAM_RECONNECTING
SharedMemoryImageError SharedMemoryImageStream_unlock(SharedMemoryImageStream stream)
SharedMemoryImageStream_unlock release a frame obtained by SharedMemoryImageStream_lock.
struct _SharedMemoryImageStream * SharedMemoryImageStream
image transport stream pointer
#define IMAGESTREAM_NOERROR
SharedMemoryImageError SharedMemoryImageStream_lock(SharedMemoryImageStream stream, SharedMemoryFrame *frame)
SharedMemoryImageStream_lock get a frame of pixels from the server.
C API the shared memory image transport API server interface.
#define IMAGESTREAM_OPTIONS_SERVER
DEV SharedMemoryImageError SharedMemoryImageStream_putframe(SharedMemoryImageStream stream, SharedMemoryFrame frame)
SharedMemoryImageStream_putframe push a new image into a server image stream channel.
SharedMemoryImageError stream_unlock(stream_id id)
Unlock the frame allocated by stream_lock().
SharedMemoryImageError stream_putframe(stream_id id, int width, int height, int framenumber, bytes pixeldata)
Push an image into the shared memory transport buffer.
stream_id stream_create(str filename, int options=IMAGESTREAM_OPTIONS_NONE)
Create a shared memory image stream.
frame stream_lock(stream_id id)
Get any available image frame.
SharedMemoryImageError stream_destroy(stream_id id)
Close down and destroy a shared memory transport interface.
uint32_t iFrame
monotonically increasing frame number
uint32_t iWidth
width of a frame in pixels
uint8_t * buffer
pointer to a packed RGBRGBRGB... (iWidth*iHeight*3) bytes array of pixels
uint32_t iHeight
height of a frame in pixels

Connect with Ansys