Skip to main content

Post-processing tools 2023 R2

shared_memory_image_client_python.c

Last update: 17.04.2023
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
31 static SharedMemoryImageStream streamlist[MAX_STREAMS] = { 0 };
80 #ifdef DOXYGEN
95 stream_id stream_create(str filename, int options=IMAGESTREAM_OPTIONS_NONE);
96 
107 frame stream_lock(stream_id id);
108 
120 
130 
145 SharedMemoryImageError stream_putframe(stream_id id, int width, int height, int framenumber, bytes pixeldata);
146 
147 #endif
148 
149 /* Utility functions PyDict_SetItemString with DECREF */
150 static 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
158 static 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 
167 static 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 
193 static 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 
217 static 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 
231 static 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 
268 static 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 
283 static 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 */
294 static PyObject *pmodule = NULL;
295 static 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 
311 static 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 
323 PyMODINIT_FUNC PyInit_ensight_grpc_shmem()
324 {
325  pmodule = PyModule_Create(&moduledef);
326 #else
327 PyMODINIT_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
#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.
const char * SharedMemoryImageStream_errorstring(SharedMemoryImageError err)
SharedMemoryImageStream_errorstring convert an error code into a human string.
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