blender/source/gameengine/VideoTexture/DeckLink.cpp
2016-07-02 10:08:33 +10:00

814 lines
24 KiB
C++

/*
* ***** 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 <memory.h>
// 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<DeckLink*>(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<PyObject*>(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 <mode>[/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<char**>(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", &param, &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<PyObject*>(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<PyImage*>(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<PyObject*>(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<PyImage*>(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 */