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 }