bgl.Buffer() to bytes
I actually solved the problem. I have now >30fps. The solution was the following:

Change

imageData = np.array(currentImage, dtype=np.uint8)


to

imageData = np.asarray(currentImage, dtype=np.uint8)


will let numpy try to get the values using the buffer protocol of numpy (maybe np.array will also do this, didn't check). For more informations about the buffer protocol: https://docs.python.org/3/c-api/buffer.html

Then I extended the source code of Blender by implementing the buffer protocol in the bgl.Buffer code. Now, the numpy call will access the buffer of the given buffer object.

The following lines will need to be inserted into the bgl.c source file:

static int itemsize_by_buffer_type(int buffer_type)
{
    if (buffer_type == GL_BYTE) return sizeof(GLbyte);
    if (buffer_type == GL_SHORT) return sizeof(GLshort);
    if (buffer_type == GL_INT) return sizeof(GLint);
    if (buffer_type == GL_FLOAT) return sizeof(GLfloat);
    return -1;  /* should never happen */
}

static const char *bp_format_from_buffer_type(int type)
{
    if (type == GL_BYTE) return "b";
    if (type == GL_SHORT) return "h";
    if (type == GL_INT) return "i";
    if (type == GL_FLOAT) return "f";
    return NULL;
}

static int BPy_Buffer_getbuffer(Buffer *self, Py_buffer *view, int flags)
{
    void* buffer = self-&gt;buf.asvoid;
    int itemsize = itemsize_by_buffer_type(self-&gt;type);
    // Number of entries in the buffer
    const unsigned long n = *self-&gt;dimensions;
    unsigned long length = itemsize * n;

if (PyBuffer_FillInfo(view, (PyObject *)self, buffer, length, false,     flags) == -1) {
        return -1;
    }

    view-&gt;itemsize = itemsize;
    view-&gt;format = (char*)bp_format_from_buffer_type(self-&gt;type);

    Py_ssize_t *shape = MEM_mallocN(sizeof(Py_ssize_t), __func__);
    shape[0] = n;
    view-&gt;shape = shape;

    return 0;
}

static void BPy_Buffer_releasebuffer(Buffer *UNUSED(self), Py_buffer *view)
{
    MEM_freeN(view-&gt;shape);
}

static PyBufferProcs BPy_Buffer_Buffer = {
    (getbufferproc)BPy_Buffer_getbuffer,
    (releasebufferproc)BPy_Buffer_releasebuffer,
};


Last, but not least, you need to change the following line:

/* Functions to access object as input/output buffer */
NULL,         /* PyBufferProcs *tp_as_buffer; */


into

/* Functions to access object as input/output buffer */
&amp;BPy_Buffer_Buffer,         /* PyBufferProcs *tp_as_buffer; */


I also created a patch for that and I'll hope that this will make its way into a future version of Blender: https://developer.blender.org/D2734