1 module decklink4d.utils; 2 public import decklink4d.port; 3 public import decklink4d.bmd.decklinkapi; 4 import std.bitmanip; 5 import std.traits; 6 import std.stdio; 7 8 // *************************** global functions 9 version(Windows) 10 { 11 IDeckLinkIterator CreateDeckLinkIteratorInstance () 12 { 13 IDeckLinkIterator p; 14 HRESULT hr=CoCreateInstance(&CLSID_CDeckLinkIterator, null, CLSCTX_ALL, &IID_IDeckLinkIterator, &p); 15 return (hr == S_OK) ? p : null; 16 } 17 IDeckLinkGLScreenPreviewHelper CreateOpenGLScreenPreviewHelper() 18 { 19 IDeckLinkGLScreenPreviewHelper p; 20 HRESULT hr=CoCreateInstance(&CLSID_CDeckLinkGLScreenPreviewHelper, null, CLSCTX_ALL, &IID_IDeckLinkGLScreenPreviewHelper, &p); 21 return (hr == S_OK) ? p : null; 22 } 23 } 24 else 25 { 26 extern(System) { 27 IDeckLinkIterator CreateDeckLinkIteratorInstance (); 28 IDeckLinkGLScreenPreviewHelper CreateOpenGLScreenPreviewHelper (); 29 IDeckLinkCocoaScreenPreviewCallback CreateCocoaScreenPreview (void* /* (NSView*) */ parentView); 30 IDeckLinkVideoConversion CreateVideoConversionInstance (); 31 } 32 } 33 34 // *************************** COM helper classes 35 struct ComPtr(T) 36 { 37 // takes ownership by default 38 this(T o, bool addref = false) 39 { 40 obj = o; 41 if (!isNull) 42 { 43 if (addref) 44 obj.AddRef(); 45 trace("comobj %s: %s", T.stringof, cast(void*)obj); 46 } 47 } 48 ~this() 49 { 50 if (!isNull) 51 { 52 auto count = obj.Release(); 53 trace("destructing %s: %s => %s", T.stringof, cast(void*)obj, count); 54 } 55 } 56 this(this) 57 { 58 if (!isNull) 59 { 60 auto count = obj.AddRef(); 61 trace("postblt %s: %s => %s", T.stringof, cast(void*)obj, count); 62 } 63 } 64 @property bool isNull() 65 { 66 return obj is null; 67 } 68 int opDispatch(string op, T...)(T args) 69 { 70 static assert(op != "QueryInterface" && op != "AddRef" && op != "Release"); 71 return mixin("obj."~op~"(args)"); 72 } 73 74 ComPtr!I comcast(I)() 75 { 76 void* p = null; 77 auto pp = &p; 78 HRESULT hr = obj.QueryInterface(getGuid(I.iid), pp); 79 if (hr != S_OK) 80 throw new Exception("no interface"); 81 I target = cast(I)p; 82 return ComPtr!I(target); 83 } 84 @property T ptr() 85 { 86 return obj; 87 } 88 @property T* outArg() 89 { 90 if (!isNull) 91 { 92 auto count = obj.Release(); 93 trace("destructing %s: %s => %s", T.stringof, cast(void*)obj, count); 94 } 95 return &obj; 96 } 97 private: 98 T obj; 99 100 version(Windows) 101 { 102 // Windows QueryInterface needs a pointer 103 auto getGuid(const ref GUID guid) { return &guid; } 104 } 105 else 106 { 107 // DeckLinkAPI Mac GUIDs are endian swapped 108 auto getGuid(in GUID guid) 109 { 110 const GUID endianSwapped = { swapEndian(guid.Data1), swapEndian(guid.Data2), swapEndian(guid.Data3), guid.Data4 }; 111 return endianSwapped; 112 } 113 } 114 } 115 116 class SmartIterator(ITERATOR) 117 { 118 public: 119 // takes ownership 120 this(ITERATOR it) 121 { 122 m_iterator = ComPtr!ITERATOR(it); 123 m_next = getNext(); 124 } 125 this(ComPtr!ITERATOR it) 126 { 127 m_iterator = it; 128 m_next = getNext(); 129 } 130 bool empty() 131 { 132 return m_next.isNull(); 133 } 134 auto front() 135 { 136 return m_next; 137 } 138 void popFront() 139 { 140 if (!(m_next.isNull)) 141 m_next = getNext(); 142 } 143 private: 144 // figure out the interface type being iterated by this iterator 145 alias PointerTarget!(ParameterTypeTuple!(ITERATOR.Next)[0]) INTERFACE; 146 147 ComPtr!ITERATOR m_iterator; 148 ComPtr!INTERFACE m_next; 149 150 ComPtr!INTERFACE getNext() 151 { 152 INTERFACE next; 153 HRESULT hr = m_iterator.Next(&next); 154 return ComPtr!INTERFACE((hr == S_OK) ? next : null); 155 } 156 } 157 158 import core.memory; 159 import core.atomic; 160 161 class ComObj : IUnknown 162 { 163 private int m_refCount = 0; 164 165 protected: 166 // derived class override this 167 HRESULT RealQueryInterface(const(IID)* riid, void** ppv) 168 { 169 if (*riid == IID_IUnknown) 170 { 171 *ppv = cast(void*)cast(IUnknown)this; 172 AddRef(); 173 return S_OK; 174 } 175 else 176 { *ppv = null; 177 return E_NOINTERFACE; 178 } 179 } 180 extern(System): 181 version(Windows) 182 { 183 override HRESULT QueryInterface(const(IID)* riid, void** ppv) 184 { 185 return RealQueryInterface(riid, ppv); 186 } 187 } 188 else 189 { 190 override HRESULT QueryInterface(IID riid, void** ppv) 191 { 192 return RealQueryInterface(&riid, ppv); 193 } 194 } 195 override uint AddRef() 196 { 197 int lRef = atomicOp!"+="(*cast(shared)&m_refCount, 1); 198 version(DEBUG) 199 { 200 scope(exit) 201 writefln("addref->%s", lRef); 202 } 203 if (lRef == 1) 204 { 205 // pin this object down 206 version(DEBUG) 207 { 208 writefln("pinned"); 209 } 210 GC.addRoot(cast(void*)this); 211 GC.setAttr(cast(void*)this, GC.BlkAttr.NO_MOVE); 212 } 213 return lRef; 214 } 215 override uint Release() 216 { 217 int lRef = atomicOp!"-="(*cast(shared)&m_refCount, 1); 218 version(DEBUG) 219 { 220 scope(exit) 221 writefln("release->%s", lRef); 222 } 223 if (lRef == 0) 224 { 225 // okay to collect now 226 writefln("unpinned"); 227 GC.removeRoot(cast(void*)this); 228 GC.clrAttr(cast(void*)this, GC.BlkAttr.NO_MOVE); 229 return 0; 230 } 231 return lRef; 232 } 233 } 234 235 auto getDefaultDevice() 236 { 237 auto i = new SmartIterator!IDeckLinkIterator(CreateDeckLinkIteratorInstance()); 238 foreach(decklink; i) 239 return decklink; 240 throw new Exception("no decklink device"); 241 }