/* * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * The Original Code is Copyright (C) 2015, Blender Foundation * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): Blender Foundation. * * ***** END GPL LICENSE BLOCK ***** */ /** \file gameengine/VideoTexture/Texture.cpp * \ingroup bgevideotex */ #ifdef WITH_GAMEENGINE_DECKLINK // implementation // FFmpeg defines its own version of stdint.h on Windows. // Decklink needs FFmpeg, so it uses its version of stdint.h // this is necessary for INT64_C macro #ifndef __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS #endif // this is necessary for UINTPTR_MAX (used by atomic-ops) #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS #endif #include "atomic_ops.h" #include "EXP_PyObjectPlus.h" #include "KX_KetsjiEngine.h" #include "KX_PythonInit.h" #include "DeckLink.h" #include // macro for exception handling and logging #define CATCH_EXCP catch (Exception & exp) \ { exp.report(); return NULL; } static struct { const char *name; BMDDisplayMode mode; } sModeStringTab[] = { { "NTSC", bmdModeNTSC }, { "NTSC2398", bmdModeNTSC2398 }, { "PAL", bmdModePAL }, { "NTSCp", bmdModeNTSCp }, { "PALp", bmdModePALp }, /* HD 1080 Modes */ { "HD1080p2398", bmdModeHD1080p2398 }, { "HD1080p24", bmdModeHD1080p24 }, { "HD1080p25", bmdModeHD1080p25 }, { "HD1080p2997", bmdModeHD1080p2997 }, { "HD1080p30", bmdModeHD1080p30 }, { "HD1080i50", bmdModeHD1080i50 }, { "HD1080i5994", bmdModeHD1080i5994 }, { "HD1080i6000", bmdModeHD1080i6000 }, { "HD1080p50", bmdModeHD1080p50 }, { "HD1080p5994", bmdModeHD1080p5994 }, { "HD1080p6000", bmdModeHD1080p6000 }, /* HD 720 Modes */ { "HD720p50", bmdModeHD720p50 }, { "HD720p5994", bmdModeHD720p5994 }, { "HD720p60", bmdModeHD720p60 }, /* 2k Modes */ { "2k2398", bmdMode2k2398 }, { "2k24", bmdMode2k24 }, { "2k25", bmdMode2k25 }, /* DCI Modes (output only) */ { "2kDCI2398", bmdMode2kDCI2398 }, { "2kDCI24", bmdMode2kDCI24 }, { "2kDCI25", bmdMode2kDCI25 }, /* 4k Modes */ { "4K2160p2398", bmdMode4K2160p2398 }, { "4K2160p24", bmdMode4K2160p24 }, { "4K2160p25", bmdMode4K2160p25 }, { "4K2160p2997", bmdMode4K2160p2997 }, { "4K2160p30", bmdMode4K2160p30 }, { "4K2160p50", bmdMode4K2160p50 }, { "4K2160p5994", bmdMode4K2160p5994 }, { "4K2160p60", bmdMode4K2160p60 }, // sentinel { NULL } }; static struct { const char *name; BMDPixelFormat format; } sFormatStringTab[] = { { "8BitYUV", bmdFormat8BitYUV }, { "10BitYUV", bmdFormat10BitYUV }, { "8BitARGB", bmdFormat8BitARGB }, { "8BitBGRA", bmdFormat8BitBGRA }, { "10BitRGB", bmdFormat10BitRGB }, { "12BitRGB", bmdFormat12BitRGB }, { "12BitRGBLE", bmdFormat12BitRGBLE }, { "10BitRGBXLE", bmdFormat10BitRGBXLE }, { "10BitRGBX", bmdFormat10BitRGBX }, // sentinel { NULL } }; ExceptionID DeckLinkBadDisplayMode, DeckLinkBadPixelFormat; ExpDesc DeckLinkBadDisplayModeDesc(DeckLinkBadDisplayMode, "Invalid or unsupported display mode"); ExpDesc DeckLinkBadPixelFormatDesc(DeckLinkBadPixelFormat, "Invalid or unsupported pixel format"); HRESULT decklink_ReadDisplayMode(const char *format, size_t len, BMDDisplayMode *displayMode) { int i; if (len == 0) len = strlen(format); for (i = 0; sModeStringTab[i].name != NULL; i++) { if (strlen(sModeStringTab[i].name) == len && !strncmp(sModeStringTab[i].name, format, len)) { *displayMode = sModeStringTab[i].mode; return S_OK; } } if (len != 4) THRWEXCP(DeckLinkBadDisplayMode, S_OK); // assume the user entered directly the mode value as a 4 char string *displayMode = (BMDDisplayMode)((((uint32_t)format[0]) << 24) + (((uint32_t)format[1]) << 16) + (((uint32_t)format[2]) << 8) + ((uint32_t)format[3])); return S_OK; } HRESULT decklink_ReadPixelFormat(const char *format, size_t len, BMDPixelFormat *pixelFormat) { int i; if (!len) len = strlen(format); for (i = 0; sFormatStringTab[i].name != NULL; i++) { if (strlen(sFormatStringTab[i].name) == len && !strncmp(sFormatStringTab[i].name, format, len)) { *pixelFormat = sFormatStringTab[i].format; return S_OK; } } if (len != 4) THRWEXCP(DeckLinkBadPixelFormat, S_OK); // assume the user entered directly the mode value as a 4 char string *pixelFormat = (BMDPixelFormat)((((uint32_t)format[0]) << 24) + (((uint32_t)format[1]) << 16) + (((uint32_t)format[2]) << 8) + ((uint32_t)format[3])); return S_OK; } class DeckLink3DFrameWrapper : public IDeckLinkVideoFrame, IDeckLinkVideoFrame3DExtensions { public: // IUnknown virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) { if (!memcmp(&iid, &IID_IDeckLinkVideoFrame3DExtensions, sizeof(iid))) { if (mpRightEye) { *ppv = (IDeckLinkVideoFrame3DExtensions*)this; return S_OK; } } return E_NOTIMPL; } virtual ULONG STDMETHODCALLTYPE AddRef(void) { return 1U; } virtual ULONG STDMETHODCALLTYPE Release(void) { return 1U; } // IDeckLinkVideoFrame virtual long STDMETHODCALLTYPE GetWidth(void) { return mpLeftEye->GetWidth(); } virtual long STDMETHODCALLTYPE GetHeight(void) { return mpLeftEye->GetHeight(); } virtual long STDMETHODCALLTYPE GetRowBytes(void) { return mpLeftEye->GetRowBytes(); } virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat(void) { return mpLeftEye->GetPixelFormat(); } virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags(void) { return mpLeftEye->GetFlags(); } virtual HRESULT STDMETHODCALLTYPE GetBytes(void **buffer) { return mpLeftEye->GetBytes(buffer); } virtual HRESULT STDMETHODCALLTYPE GetTimecode(BMDTimecodeFormat format,IDeckLinkTimecode **timecode) { return mpLeftEye->GetTimecode(format, timecode); } virtual HRESULT STDMETHODCALLTYPE GetAncillaryData(IDeckLinkVideoFrameAncillary **ancillary) { return mpLeftEye->GetAncillaryData(ancillary); } // IDeckLinkVideoFrame3DExtensions virtual BMDVideo3DPackingFormat STDMETHODCALLTYPE Get3DPackingFormat(void) { return bmdVideo3DPackingLeftOnly; } virtual HRESULT STDMETHODCALLTYPE GetFrameForRightEye( /* [out] */ IDeckLinkVideoFrame **rightEyeFrame) { mpRightEye->AddRef(); *rightEyeFrame = mpRightEye; return S_OK; } // Constructor DeckLink3DFrameWrapper(IDeckLinkVideoFrame *leftEye, IDeckLinkVideoFrame *rightEye) { mpLeftEye = leftEye; mpRightEye = rightEye; } // no need for a destructor, it's just a wrapper private: IDeckLinkVideoFrame *mpLeftEye; IDeckLinkVideoFrame *mpRightEye; }; static void decklink_Reset(DeckLink *self) { self->m_lastClock = 0.0; self->mDLOutput = NULL; self->mUse3D = false; self->mDisplayMode = bmdModeUnknown; self->mKeyingSupported = false; self->mHDKeyingSupported = false; self->mSize[0] = 0; self->mSize[1] = 0; self->mFrameSize = 0; self->mLeftFrame = NULL; self->mRightFrame = NULL; self->mKeyer = NULL; self->mUseKeying = false; self->mKeyingLevel = 255; self->mUseExtend = false; } #ifdef __BIG_ENDIAN__ #define CONV_PIXEL(i) ((((i)>>16)&0xFF00)+(((i)&0xFF00)<<16)+((i)&0xFF00FF)) #else #define CONV_PIXEL(i) ((((i)&0xFF)<<16)+(((i)>>16)&0xFF)+((i)&0xFF00FF00)) #endif // adapt the pixel format and picture size from VideoTexture (RGBA) to DeckLink (BGRA) static void decklink_ConvImage(uint32_t *dest, const short *destSize, const uint32_t *source, const short *srcSize, bool extend) { short w, h, x, y; const uint32_t *s; uint32_t *d, p; bool sameSize = (destSize[0] == srcSize[0] && destSize[1] == srcSize[1]); if (sameSize || !extend) { // here we convert pixel by pixel w = (destSize[0] < srcSize[0]) ? destSize[0] : srcSize[0]; h = (destSize[1] < srcSize[1]) ? destSize[1] : srcSize[1]; for (y = 0; y < h; ++y) { s = source + y*srcSize[0]; d = dest + y*destSize[0]; for (x = 0; x < w; ++x, ++s, ++d) { *d = CONV_PIXEL(*s); } } } else { // here we scale // interpolation accumulator int accHeight = srcSize[1] >> 1; d = dest; s = source; // process image rows for (y = 0; y < srcSize[1]; ++y) { // increase height accum accHeight += destSize[1]; // if pixel row has to be drawn if (accHeight >= srcSize[1]) { // decrease accum accHeight -= srcSize[1]; // width accum int accWidth = srcSize[0] >> 1; // process row for (x = 0; x < srcSize[0]; ++x, ++s) { // increase width accum accWidth += destSize[0]; // convert pixel p = CONV_PIXEL(*s); // if pixel has to be drown one or more times while (accWidth >= srcSize[0]) { // decrease accum accWidth -= srcSize[0]; *d++ = p; } } // if there should be more identical lines while (accHeight >= srcSize[1]) { accHeight -= srcSize[1]; // copy previous line memcpy(d, d - destSize[0], 4 * destSize[0]); d += destSize[0]; } } else { // if we skip a source line s += srcSize[0]; } } } } // DeckLink object allocation static PyObject *DeckLink_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { // allocate object DeckLink * self = reinterpret_cast(type->tp_alloc(type, 0)); // initialize object structure decklink_Reset(self); // m_leftEye is a python object, it's handled by python self->m_leftEye = NULL; self->m_rightEye = NULL; // return allocated object return reinterpret_cast(self); } // forward declaration PyObject *DeckLink_close(DeckLink *self); int DeckLink_setSource(DeckLink *self, PyObject *value, void *closure); // DeckLink object deallocation static void DeckLink_dealloc(DeckLink *self) { // release renderer Py_XDECREF(self->m_leftEye); // close decklink PyObject *ret = DeckLink_close(self); Py_DECREF(ret); // release object Py_TYPE((PyObject *)self)->tp_free((PyObject *)self); } ExceptionID AutoDetectionNotAvail, DeckLinkOpenCard, DeckLinkBadFormat, DeckLinkInternalError; ExpDesc AutoDetectionNotAvailDesc(AutoDetectionNotAvail, "Auto detection not yet available"); ExpDesc DeckLinkOpenCardDesc(DeckLinkOpenCard, "Cannot open card for output"); ExpDesc DeckLinkBadFormatDesc(DeckLinkBadFormat, "Invalid or unsupported output format, use [/3D]"); ExpDesc DeckLinkInternalErrorDesc(DeckLinkInternalError, "DeckLink API internal error, please report"); // DeckLink object initialization static int DeckLink_init(DeckLink *self, PyObject *args, PyObject *kwds) { IDeckLinkIterator* pIterator; IDeckLinkAttributes* pAttributes; IDeckLinkDisplayModeIterator* pDisplayModeIterator; IDeckLinkDisplayMode* pDisplayMode; IDeckLink* pDL; char* p3D; BOOL flag; size_t len; int i; uint32_t displayFlags; BMDVideoOutputFlags outputFlags; BMDDisplayModeSupport support; uint32_t* bytes; // material ID short cardIdx = 0; // texture ID char *format = NULL; static const char *kwlist[] = {"cardIdx", "format", NULL}; // get parameters if (!PyArg_ParseTupleAndKeywords(args, kwds, "|hs", const_cast(kwlist), &cardIdx, &format)) return -1; try { if (format == NULL) { THRWEXCP(AutoDetectionNotAvail, S_OK); } if ((p3D = strchr(format, '/')) != NULL && strcmp(p3D, "/3D")) THRWEXCP(DeckLinkBadFormat, S_OK); self->mUse3D = (p3D) ? true : false; // read the mode len = (p3D) ? (size_t)(p3D - format) : strlen(format); // throws if bad mode decklink_ReadDisplayMode(format, len, &self->mDisplayMode); pIterator = BMD_CreateDeckLinkIterator(); pDL = NULL; if (pIterator) { i = 0; while (pIterator->Next(&pDL) == S_OK) { if (i == cardIdx) { break; } i++; pDL->Release(); pDL = NULL; } pIterator->Release(); } if (!pDL) { THRWEXCP(DeckLinkOpenCard, S_OK); } // detect the capabilities if (pDL->QueryInterface(IID_IDeckLinkAttributes, (void**)&pAttributes) == S_OK) { if (pAttributes->GetFlag(BMDDeckLinkSupportsInternalKeying, &flag) == S_OK && flag) { self->mKeyingSupported = true; if (pAttributes->GetFlag(BMDDeckLinkSupportsHDKeying, &flag) == S_OK && flag) { self->mHDKeyingSupported = true; } } pAttributes->Release(); } if (pDL->QueryInterface(IID_IDeckLinkOutput, (void**)&self->mDLOutput) != S_OK) { self->mDLOutput = NULL; } if (self->mKeyingSupported) { pDL->QueryInterface(IID_IDeckLinkKeyer, (void **)&self->mKeyer); } // we don't need the device anymore, release to avoid leaking pDL->Release(); if (!self->mDLOutput) THRWEXCP(DeckLinkOpenCard, S_OK); if (self->mDLOutput->GetDisplayModeIterator(&pDisplayModeIterator) != S_OK) THRWEXCP(DeckLinkInternalError, S_OK); displayFlags = (self->mUse3D) ? bmdDisplayModeSupports3D : 0; outputFlags = (self->mUse3D) ? bmdVideoOutputDualStream3D : bmdVideoOutputFlagDefault; pDisplayMode = NULL; i = 0; while (pDisplayModeIterator->Next(&pDisplayMode) == S_OK) { if (pDisplayMode->GetDisplayMode() == self->mDisplayMode && (pDisplayMode->GetFlags() & displayFlags) == displayFlags) { if (self->mDLOutput->DoesSupportVideoMode(self->mDisplayMode, bmdFormat8BitBGRA, outputFlags, &support, NULL) != S_OK || support == bmdDisplayModeNotSupported) { printf("Warning: DeckLink card %d reports no BGRA support, proceed anyway\n", cardIdx); } break; } pDisplayMode->Release(); pDisplayMode = NULL; i++; } pDisplayModeIterator->Release(); if (!pDisplayMode) THRWEXCP(DeckLinkBadFormat, S_OK); self->mSize[0] = pDisplayMode->GetWidth(); self->mSize[1] = pDisplayMode->GetHeight(); self->mFrameSize = 4*self->mSize[0]*self->mSize[1]; pDisplayMode->Release(); if (self->mDLOutput->EnableVideoOutput(self->mDisplayMode, outputFlags) != S_OK) // this shouldn't fail THRWEXCP(DeckLinkOpenCard, S_OK); if (self->mDLOutput->CreateVideoFrame(self->mSize[0], self->mSize[1], self->mSize[0] * 4, bmdFormat8BitBGRA, bmdFrameFlagFlipVertical, &self->mLeftFrame) != S_OK) THRWEXCP(DeckLinkInternalError, S_OK); // clear alpha channel in the frame buffer self->mLeftFrame->GetBytes((void **)&bytes); memset(bytes, 0, self->mFrameSize); if (self->mUse3D) { if (self->mDLOutput->CreateVideoFrame(self->mSize[0], self->mSize[1], self->mSize[0] * 4, bmdFormat8BitBGRA, bmdFrameFlagFlipVertical, &self->mRightFrame) != S_OK) THRWEXCP(DeckLinkInternalError, S_OK); // clear alpha channel in the frame buffer self->mRightFrame->GetBytes((void **)&bytes); memset(bytes, 0, self->mFrameSize); } } catch (Exception & exp) { printf("DeckLink: exception when opening card %d: %s\n", cardIdx, exp.what()); exp.report(); // normally, the object should be deallocated return -1; } // initialization succeeded return 0; } // close added decklink PyObject *DeckLink_close(DeckLink * self) { if (self->mLeftFrame) self->mLeftFrame->Release(); if (self->mRightFrame) self->mRightFrame->Release(); if (self->mKeyer) self->mKeyer->Release(); if (self->mDLOutput) self->mDLOutput->Release(); decklink_Reset(self); Py_RETURN_NONE; } // refresh decklink key frame static PyObject *DeckLink_refresh(DeckLink *self, PyObject *args) { // get parameter - refresh source PyObject *param; double ts = -1.0; if (!PyArg_ParseTuple(args, "O|d:refresh", ¶m, &ts) || !PyBool_Check(param)) { // report error PyErr_SetString(PyExc_TypeError, "The value must be a bool"); return NULL; } // some trick here: we are in the business of loading a key frame in decklink, // no use to do it if we are still in the same rendering frame. // We find this out by looking at the engine current clock time KX_KetsjiEngine* engine = KX_GetActiveEngine(); if (engine->GetClockTime() != self->m_lastClock) { self->m_lastClock = engine->GetClockTime(); // set source refresh bool refreshSource = (param == Py_True); uint32_t *leftEye = NULL; uint32_t *rightEye = NULL; // try to process key frame from source try { // check if optimization is possible if (self->m_leftEye != NULL) { ImageBase *leftImage = self->m_leftEye->m_image; short * srcSize = leftImage->getSize(); self->mLeftFrame->GetBytes((void **)&leftEye); if (srcSize[0] == self->mSize[0] && srcSize[1] == self->mSize[1]) { // buffer has same size, can load directly if (!leftImage->loadImage(leftEye, self->mFrameSize, GL_BGRA, ts)) leftEye = NULL; } else { // scaling is required, go the hard way unsigned int *src = leftImage->getImage(0, ts); if (src != NULL) decklink_ConvImage(leftEye, self->mSize, src, srcSize, self->mUseExtend); else leftEye = NULL; } } if (leftEye) { if (self->mUse3D && self->m_rightEye != NULL) { ImageBase *rightImage = self->m_rightEye->m_image; short * srcSize = rightImage->getSize(); self->mRightFrame->GetBytes((void **)&rightEye); if (srcSize[0] == self->mSize[0] && srcSize[1] == self->mSize[1]) { // buffer has same size, can load directly rightImage->loadImage(rightEye, self->mFrameSize, GL_BGRA, ts); } else { // scaling is required, go the hard way unsigned int *src = rightImage->getImage(0, ts); if (src != NULL) decklink_ConvImage(rightEye, self->mSize, src, srcSize, self->mUseExtend); } } if (self->mUse3D) { DeckLink3DFrameWrapper frame3D( (IDeckLinkVideoFrame*)self->mLeftFrame, (IDeckLinkVideoFrame*)self->mRightFrame); self->mDLOutput->DisplayVideoFrameSync(&frame3D); } else { self->mDLOutput->DisplayVideoFrameSync((IDeckLinkVideoFrame*)self->mLeftFrame); } } // refresh texture source, if required if (refreshSource) { if (self->m_leftEye) self->m_leftEye->m_image->refresh(); if (self->m_rightEye) self->m_rightEye->m_image->refresh(); } } CATCH_EXCP; } Py_RETURN_NONE; } // get source object static PyObject *DeckLink_getSource(DeckLink *self, PyObject *value, void *closure) { // if source exists if (self->m_leftEye != NULL) { Py_INCREF(self->m_leftEye); return reinterpret_cast(self->m_leftEye); } // otherwise return None Py_RETURN_NONE; } // set source object int DeckLink_setSource(DeckLink *self, PyObject *value, void *closure) { // check new value if (value == NULL || !pyImageTypes.in(Py_TYPE(value))) { // report value error PyErr_SetString(PyExc_TypeError, "Invalid type of value"); return -1; } // increase ref count for new value Py_INCREF(value); // release previous Py_XDECREF(self->m_leftEye); // set new value self->m_leftEye = reinterpret_cast(value); // return success return 0; } // get source object static PyObject *DeckLink_getRight(DeckLink *self, PyObject *value, void *closure) { // if source exists if (self->m_rightEye != NULL) { Py_INCREF(self->m_rightEye); return reinterpret_cast(self->m_rightEye); } // otherwise return None Py_RETURN_NONE; } // set source object static int DeckLink_setRight(DeckLink *self, PyObject *value, void *closure) { // check new value if (value == NULL || !pyImageTypes.in(Py_TYPE(value))) { // report value error PyErr_SetString(PyExc_TypeError, "Invalid type of value"); return -1; } // increase ref count for new value Py_INCREF(value); // release previous Py_XDECREF(self->m_rightEye); // set new value self->m_rightEye = reinterpret_cast(value); // return success return 0; } static PyObject *DeckLink_getKeying(DeckLink *self, PyObject *value, void *closure) { if (self->mUseKeying) Py_RETURN_TRUE; else Py_RETURN_FALSE; } static int DeckLink_setKeying(DeckLink *self, PyObject *value, void *closure) { if (value == NULL || !PyBool_Check(value)) { PyErr_SetString(PyExc_TypeError, "The value must be a bool"); return -1; } if (self->mKeyer != NULL) { if (value == Py_True) { if (self->mKeyer->Enable(false) != S_OK) { PyErr_SetString(PyExc_RuntimeError, "Error enabling keyer"); return -1; } self->mUseKeying = true; self->mKeyer->SetLevel(self->mKeyingLevel); } else { self->mKeyer->Disable(); self->mUseKeying = false; } } // success return 0; } static PyObject *DeckLink_getLevel(DeckLink *self, PyObject *value, void *closure) { return Py_BuildValue("h", self->mKeyingLevel); } static int DeckLink_setLevel(DeckLink *self, PyObject *value, void *closure) { long level; if (value == NULL || !PyLong_Check(value)) { PyErr_SetString(PyExc_TypeError, "The value must be an integer from 0 to 255"); return -1; } level = PyLong_AsLong(value); if (level > 255) level = 255; else if (level < 0) level = 0; self->mKeyingLevel = (uint8_t)level; if (self->mUseKeying) { if (self->mKeyer->SetLevel(self->mKeyingLevel) != S_OK) { PyErr_SetString(PyExc_RuntimeError, "Error changin level of keyer"); return -1; } } // success return 0; } static PyObject *DeckLink_getExtend(DeckLink *self, PyObject *value, void *closure) { if (self->mUseExtend) Py_RETURN_TRUE; else Py_RETURN_FALSE; } static int DeckLink_setExtend(DeckLink *self, PyObject *value, void *closure) { if (value == NULL || !PyBool_Check(value)) { PyErr_SetString(PyExc_TypeError, "The value must be a bool"); return -1; } self->mUseExtend = (value == Py_True); return 0; } // class DeckLink methods static PyMethodDef decklinkMethods[] = { { "close", (PyCFunction)DeckLink_close, METH_NOARGS, "Close dynamic decklink and restore original"}, { "refresh", (PyCFunction)DeckLink_refresh, METH_VARARGS, "Refresh decklink from source"}, {NULL} /* Sentinel */ }; // class DeckLink attributes static PyGetSetDef decklinkGetSets[] = { { (char*)"source", (getter)DeckLink_getSource, (setter)DeckLink_setSource, (char*)"source of decklink (left eye)", NULL}, { (char*)"right", (getter)DeckLink_getRight, (setter)DeckLink_setRight, (char*)"source of decklink (right eye)", NULL }, { (char*)"keying", (getter)DeckLink_getKeying, (setter)DeckLink_setKeying, (char*)"whether keying is enabled (frame is alpha-composited with passthrough output)", NULL }, { (char*)"level", (getter)DeckLink_getLevel, (setter)DeckLink_setLevel, (char*)"change the level of keying (overall alpha level of key frame, 0 to 255)", NULL }, { (char*)"extend", (getter)DeckLink_getExtend, (setter)DeckLink_setExtend, (char*)"whether image should stretched to fit frame", NULL }, { NULL } }; // class DeckLink declaration PyTypeObject DeckLinkType = { PyVarObject_HEAD_INIT(NULL, 0) "VideoTexture.DeckLink", /*tp_name*/ sizeof(DeckLink), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)DeckLink_dealloc,/*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ &imageBufferProcs, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ "DeckLink objects", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ decklinkMethods, /* tp_methods */ 0, /* tp_members */ decklinkGetSets, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)DeckLink_init, /* tp_init */ 0, /* tp_alloc */ DeckLink_new, /* tp_new */ }; #endif /* WITH_GAMEENGINE_DECKLINK */