decklink: Fix compiler warning with gcc
[platform/upstream/gstreamer.git] / sys / decklink / gstdecklink.cpp
1 /* GStreamer
2  * Copyright (C) 2011 David Schleef <ds@schleef.org>
3  * Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
4  * Copyright (C) 2015 Florian Langlois <florian.langlois@fr.thalesgroup.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
19  * Boston, MA 02110-1335, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <gst/gst.h>
27 #include "gstdecklink.h"
28 #include "gstdecklinkaudiosink.h"
29 #include "gstdecklinkvideosink.h"
30 #include "gstdecklinkaudiosrc.h"
31 #include "gstdecklinkvideosrc.h"
32
33 GST_DEBUG_CATEGORY_STATIC (gst_decklink_debug);
34 #define GST_CAT_DEFAULT gst_decklink_debug
35
36 GType
37 gst_decklink_mode_get_type (void)
38 {
39   static gsize id = 0;
40   static const GEnumValue modes[] = {
41     {GST_DECKLINK_MODE_AUTO, "auto", "Automatic detection"},
42
43     {GST_DECKLINK_MODE_NTSC, "ntsc", "NTSC SD 60i"},
44     {GST_DECKLINK_MODE_NTSC2398, "ntsc2398", "NTSC SD 60i (24 fps)"},
45     {GST_DECKLINK_MODE_PAL, "pal", "PAL SD 50i"},
46     {GST_DECKLINK_MODE_NTSC_P, "ntsc-p", "NTSC SD 60p"},
47     {GST_DECKLINK_MODE_PAL_P, "pal-p", "PAL SD 50p"},
48
49     {GST_DECKLINK_MODE_1080p2398, "1080p2398", "HD1080 23.98p"},
50     {GST_DECKLINK_MODE_1080p24, "1080p24", "HD1080 24p"},
51     {GST_DECKLINK_MODE_1080p25, "1080p25", "HD1080 25p"},
52     {GST_DECKLINK_MODE_1080p2997, "1080p2997", "HD1080 29.97p"},
53     {GST_DECKLINK_MODE_1080p30, "1080p30", "HD1080 30p"},
54
55     {GST_DECKLINK_MODE_1080i50, "1080i50", "HD1080 50i"},
56     {GST_DECKLINK_MODE_1080i5994, "1080i5994", "HD1080 59.94i"},
57     {GST_DECKLINK_MODE_1080i60, "1080i60", "HD1080 60i"},
58
59     {GST_DECKLINK_MODE_1080p50, "1080p50", "HD1080 50p"},
60     {GST_DECKLINK_MODE_1080p5994, "1080p5994", "HD1080 59.94p"},
61     {GST_DECKLINK_MODE_1080p60, "1080p60", "HD1080 60p"},
62
63     {GST_DECKLINK_MODE_720p50, "720p50", "HD720 50p"},
64     {GST_DECKLINK_MODE_720p5994, "720p5994", "HD720 59.94p"},
65     {GST_DECKLINK_MODE_720p60, "720p60", "HD720 60p"},
66
67     {GST_DECKLINK_MODE_2048p2398, "2048p2398", "2k 23.98p"},
68     {GST_DECKLINK_MODE_2048p24, "2048p24", "2k 24p"},
69     {GST_DECKLINK_MODE_2048p25, "2048p25", "2k 25p"},
70
71     {GST_DECKLINK_MODE_3184p2398, "3184p2398", "4k 23.98p"},
72     {GST_DECKLINK_MODE_3184p24, "3184p24", "4k 24p"},
73     {GST_DECKLINK_MODE_3184p25, "3184p25", "4k 25p"},
74     {GST_DECKLINK_MODE_3184p2997, "3184p2997", "4k 29.97p"},
75     {GST_DECKLINK_MODE_3184p30, "3184p30", "4k 30p"},
76     {GST_DECKLINK_MODE_3184p50, "3184p50", "4k 50p"},
77     {GST_DECKLINK_MODE_3184p5994, "3184p5994", "4k 59.94p"},
78     {GST_DECKLINK_MODE_3184p60, "3184p60", "4k 60p"},
79
80     {0, NULL, NULL}
81   };
82
83   if (g_once_init_enter (&id)) {
84     GType tmp = g_enum_register_static ("GstDecklinkModes", modes);
85     g_once_init_leave (&id, tmp);
86   }
87
88   return (GType) id;
89 }
90
91 GType
92 gst_decklink_connection_get_type (void)
93 {
94   static gsize id = 0;
95   static const GEnumValue connections[] = {
96     {GST_DECKLINK_CONNECTION_AUTO, "auto", "Auto"},
97     {GST_DECKLINK_CONNECTION_SDI, "sdi", "SDI"},
98     {GST_DECKLINK_CONNECTION_HDMI, "hdmi", "HDMI"},
99     {GST_DECKLINK_CONNECTION_OPTICAL_SDI, "optical-sdi", "Optical SDI"},
100     {GST_DECKLINK_CONNECTION_COMPONENT, "component", "Component"},
101     {GST_DECKLINK_CONNECTION_COMPOSITE, "composite", "Composite"},
102     {GST_DECKLINK_CONNECTION_SVIDEO, "svideo", "S-Video"},
103     {0, NULL, NULL}
104   };
105
106   if (g_once_init_enter (&id)) {
107     GType tmp = g_enum_register_static ("GstDecklinkConnection", connections);
108     g_once_init_leave (&id, tmp);
109   }
110
111   return (GType) id;
112 }
113
114 GType
115 gst_decklink_audio_connection_get_type (void)
116 {
117   static gsize id = 0;
118   static const GEnumValue connections[] = {
119     {GST_DECKLINK_AUDIO_CONNECTION_AUTO, "auto", "Automatic"},
120     {GST_DECKLINK_AUDIO_CONNECTION_EMBEDDED, "embedded",
121         "SDI/HDMI embedded audio"},
122     {GST_DECKLINK_AUDIO_CONNECTION_AES_EBU, "aes", "AES/EBU input"},
123     {GST_DECKLINK_AUDIO_CONNECTION_ANALOG, "analog", "Analog input"},
124     {GST_DECKLINK_AUDIO_CONNECTION_ANALOG_XLR, "analog-xlr",
125         "Analog input (XLR)"},
126     {GST_DECKLINK_AUDIO_CONNECTION_ANALOG_RCA, "analog-rca",
127         "Analog input (RCA)"},
128     {0, NULL, NULL}
129   };
130
131   if (g_once_init_enter (&id)) {
132     GType tmp =
133         g_enum_register_static ("GstDecklinkAudioConnection", connections);
134     g_once_init_leave (&id, tmp);
135   }
136
137   return (GType) id;
138 }
139
140 #define NTSC 10, 11, false, "bt601"
141 #define PAL 12, 11, true, "bt601"
142 #define HD 1, 1, false, "bt709"
143 #define UHD 1, 1, false, "bt2020"
144
145 static const GstDecklinkMode modes[] = {
146   {bmdModeNTSC, 720, 486, 30000, 1001, true, NTSC},     // default is ntsc
147
148   {bmdModeNTSC, 720, 486, 30000, 1001, true, NTSC},
149   {bmdModeNTSC2398, 720, 486, 24000, 1001, true, NTSC},
150   {bmdModePAL, 720, 576, 25, 1, true, PAL},
151   {bmdModeNTSCp, 720, 486, 30000, 1001, false, NTSC},
152   {bmdModePALp, 720, 576, 25, 1, false, PAL},
153
154   {bmdModeHD1080p2398, 1920, 1080, 24000, 1001, false, HD},
155   {bmdModeHD1080p24, 1920, 1080, 24, 1, false, HD},
156   {bmdModeHD1080p25, 1920, 1080, 25, 1, false, HD},
157   {bmdModeHD1080p2997, 1920, 1080, 30000, 1001, false, HD},
158   {bmdModeHD1080p30, 1920, 1080, 30, 1, false, HD},
159
160   {bmdModeHD1080i50, 1920, 1080, 50, 1, true, HD},
161   {bmdModeHD1080i5994, 1920, 1080, 60000, 1001, true, HD},
162   {bmdModeHD1080i6000, 1920, 1080, 60, 1, true, HD},
163
164   {bmdModeHD1080p50, 1920, 1080, 50, 1, false, HD},
165   {bmdModeHD1080p5994, 1920, 1080, 60000, 1001, false, HD},
166   {bmdModeHD1080p6000, 1920, 1080, 60, 1, false, HD},
167
168   {bmdModeHD720p50, 1280, 720, 50, 1, false, HD},
169   {bmdModeHD720p5994, 1280, 720, 60000, 1001, false, HD},
170   {bmdModeHD720p60, 1280, 720, 60, 1, false, HD},
171
172   {bmdMode2k2398, 2048, 1556, 24000, 1001, false, HD},
173   {bmdMode2k24, 2048, 1556, 24, 1, false, HD},
174   {bmdMode2k25, 2048, 1556, 25, 1, false, HD},
175
176   {bmdMode4K2160p2398, 3840, 2160, 24000, 1001, false, UHD},
177   {bmdMode4K2160p24, 3840, 2160, 24, 1, false, UHD},
178   {bmdMode4K2160p25, 3840, 2160, 25, 1, false, UHD},
179   {bmdMode4K2160p2997, 3840, 2160, 30000, 1001, false, UHD},
180   {bmdMode4K2160p30, 3840, 2160, 30, 1, false, UHD},
181   {bmdMode4K2160p50, 3840, 2160, 55, 1, false, UHD},
182   {bmdMode4K2160p5994, 3840, 2160, 60000, 1001, false, UHD},
183   {bmdMode4K2160p60, 3840, 2160, 60, 1, false, UHD}
184 };
185
186 const GstDecklinkMode *
187 gst_decklink_get_mode (GstDecklinkModeEnum e)
188 {
189   if (e < GST_DECKLINK_MODE_AUTO || e > GST_DECKLINK_MODE_3184p60)
190     return NULL;
191   return &modes[e];
192 }
193
194 const GstDecklinkModeEnum
195 gst_decklink_get_mode_enum_from_bmd (BMDDisplayMode mode)
196 {
197   GstDecklinkModeEnum displayMode = GST_DECKLINK_MODE_NTSC;
198   switch (mode) {
199     case bmdModeNTSC:
200       displayMode = GST_DECKLINK_MODE_NTSC;
201       break;
202     case bmdModeNTSC2398:
203       displayMode = GST_DECKLINK_MODE_NTSC2398;
204       break;
205     case bmdModePAL:
206       displayMode = GST_DECKLINK_MODE_PAL;
207       break;
208     case bmdModeNTSCp:
209       displayMode = GST_DECKLINK_MODE_NTSC_P;
210       break;
211     case bmdModePALp:
212       displayMode = GST_DECKLINK_MODE_PAL_P;
213       break;
214     case bmdModeHD1080p2398:
215       displayMode = GST_DECKLINK_MODE_1080p2398;
216       break;
217     case bmdModeHD1080p24:
218       displayMode = GST_DECKLINK_MODE_1080p24;
219       break;
220     case bmdModeHD1080p25:
221       displayMode = GST_DECKLINK_MODE_1080p25;
222       break;
223     case bmdModeHD1080p2997:
224       displayMode = GST_DECKLINK_MODE_1080p2997;
225       break;
226     case bmdModeHD1080p30:
227       displayMode = GST_DECKLINK_MODE_1080p30;
228       break;
229     case bmdModeHD1080i50:
230       displayMode = GST_DECKLINK_MODE_1080i50;
231       break;
232     case bmdModeHD1080i5994:
233       displayMode = GST_DECKLINK_MODE_1080i5994;
234       break;
235     case bmdModeHD1080i6000:
236       displayMode = GST_DECKLINK_MODE_1080i60;
237       break;
238     case bmdModeHD1080p50:
239       displayMode = GST_DECKLINK_MODE_1080p50;
240       break;
241     case bmdModeHD1080p5994:
242       displayMode = GST_DECKLINK_MODE_1080p5994;
243       break;
244     case bmdModeHD1080p6000:
245       displayMode = GST_DECKLINK_MODE_1080p60;
246       break;
247     case bmdModeHD720p50:
248       displayMode = GST_DECKLINK_MODE_720p50;
249       break;
250     case bmdModeHD720p5994:
251       displayMode = GST_DECKLINK_MODE_720p5994;
252       break;
253     case bmdModeHD720p60:
254       displayMode = GST_DECKLINK_MODE_720p60;
255       break;
256     case bmdMode2k2398:
257       displayMode = GST_DECKLINK_MODE_2048p2398;
258       break;
259     case bmdMode2k24:
260       displayMode = GST_DECKLINK_MODE_2048p24;
261       break;
262     case bmdMode2k25:
263       displayMode = GST_DECKLINK_MODE_2048p25;
264       break;
265     case bmdMode4K2160p2398:
266       displayMode = GST_DECKLINK_MODE_3184p2398;
267       break;
268     case bmdMode4K2160p24:
269       displayMode = GST_DECKLINK_MODE_3184p24;
270       break;
271     case bmdMode4K2160p25:
272       displayMode = GST_DECKLINK_MODE_3184p25;
273       break;
274     case bmdMode4K2160p2997:
275       displayMode = GST_DECKLINK_MODE_3184p2997;
276       break;
277     case bmdMode4K2160p30:
278       displayMode = GST_DECKLINK_MODE_3184p30;
279       break;
280     case bmdMode4K2160p50:
281       displayMode = GST_DECKLINK_MODE_3184p50;
282       break;
283     case bmdMode4K2160p5994:
284       displayMode = GST_DECKLINK_MODE_3184p5994;
285       break;
286     case bmdMode4K2160p60:
287       displayMode = GST_DECKLINK_MODE_3184p60;
288       break;
289     default:
290       g_assert_not_reached ();
291       break;
292   }
293   return displayMode;
294 }
295
296 static const BMDVideoConnection connections[] = {
297   0,                            /* auto */
298   bmdVideoConnectionSDI,
299   bmdVideoConnectionHDMI,
300   bmdVideoConnectionOpticalSDI,
301   bmdVideoConnectionComponent,
302   bmdVideoConnectionComposite,
303   bmdVideoConnectionSVideo
304 };
305
306 const BMDVideoConnection
307 gst_decklink_get_connection (GstDecklinkConnectionEnum e)
308 {
309   g_return_val_if_fail (e != GST_DECKLINK_CONNECTION_AUTO,
310       bmdVideoConnectionSDI);
311
312   if (e <= GST_DECKLINK_CONNECTION_AUTO || e > GST_DECKLINK_CONNECTION_SVIDEO)
313     e = GST_DECKLINK_CONNECTION_SDI;
314
315   return connections[e];
316 }
317
318 static GstStructure *
319 gst_decklink_mode_get_structure (GstDecklinkModeEnum e)
320 {
321   const GstDecklinkMode *mode = &modes[e];
322
323   return gst_structure_new ("video/x-raw",
324       "format", G_TYPE_STRING, "UYVY",
325       "width", G_TYPE_INT, mode->width,
326       "height", G_TYPE_INT, mode->height,
327       "framerate", GST_TYPE_FRACTION, mode->fps_n, mode->fps_d,
328       "interlace-mode", G_TYPE_STRING,
329       mode->interlaced ? "interleaved" : "progressive", "pixel-aspect-ratio",
330       GST_TYPE_FRACTION, mode->par_n, mode->par_d, "colorimetry", G_TYPE_STRING,
331       mode->colorimetry, "chroma-site", G_TYPE_STRING, "mpeg2", NULL);
332 }
333
334 GstCaps *
335 gst_decklink_mode_get_caps (GstDecklinkModeEnum e)
336 {
337   GstCaps *caps;
338
339   caps = gst_caps_new_empty ();
340   gst_caps_append_structure (caps, gst_decklink_mode_get_structure (e));
341
342   return caps;
343 }
344
345 GstCaps *
346 gst_decklink_mode_get_template_caps (void)
347 {
348   int i;
349   GstCaps *caps;
350   GstStructure *s;
351
352   caps = gst_caps_new_empty ();
353   for (i = 0; i < (int) G_N_ELEMENTS (modes); i++) {
354     s = gst_decklink_mode_get_structure ((GstDecklinkModeEnum) i);
355     gst_caps_append_structure (caps, s);
356   }
357
358   return caps;
359 }
360
361 #define GST_TYPE_DECKLINK_CLOCK \
362   (gst_decklink_clock_get_type())
363 #define GST_DECKLINK_CLOCK(obj) \
364   (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DECKLINK_CLOCK,GstDecklinkClock))
365 #define GST_DECKLINK_CLOCK_CLASS(klass) \
366   (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DECKLINK_CLOCK,GstDecklinkClockClass))
367 #define GST_IS_Decklink_CLOCK(obj) \
368   (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DECKLINK_CLOCK))
369 #define GST_IS_Decklink_CLOCK_CLASS(klass) \
370   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DECKLINK_CLOCK))
371 #define GST_DECKLINK_CLOCK_CAST(obj) \
372   ((GstDecklinkClock*)(obj))
373
374 typedef struct _GstDecklinkClock GstDecklinkClock;
375 typedef struct _GstDecklinkClockClass GstDecklinkClockClass;
376
377 struct _GstDecklinkClock
378 {
379   GstSystemClock clock;
380
381   GstDecklinkInput *input;
382   GstDecklinkOutput *output;
383 };
384
385 struct _GstDecklinkClockClass
386 {
387   GstSystemClockClass parent_class;
388 };
389
390 GType gst_decklink_clock_get_type (void);
391 static GstClock *gst_decklink_clock_new (const gchar * name);
392
393 typedef struct _Device Device;
394 struct _Device
395 {
396   GstDecklinkOutput output;
397   GstDecklinkInput input;
398 };
399
400 class GStreamerDecklinkInputCallback:public IDeckLinkInputCallback
401 {
402 private:
403   GstDecklinkInput * m_input;
404   GMutex m_mutex;
405   gint m_refcount;
406 public:
407     GStreamerDecklinkInputCallback (GstDecklinkInput * input)
408   : IDeckLinkInputCallback (), m_refcount (1)
409   {
410     m_input = input;
411     g_mutex_init (&m_mutex);
412   }
413
414   virtual ~ GStreamerDecklinkInputCallback ()
415   {
416     g_mutex_clear (&m_mutex);
417   }
418
419   virtual HRESULT STDMETHODCALLTYPE QueryInterface (REFIID, LPVOID *)
420   {
421     return E_NOINTERFACE;
422   }
423
424   virtual ULONG STDMETHODCALLTYPE AddRef (void)
425   {
426     ULONG ret;
427
428     g_mutex_lock (&m_mutex);
429     m_refcount++;
430     ret = m_refcount;
431     g_mutex_unlock (&m_mutex);
432
433     return ret;
434   }
435
436   virtual ULONG STDMETHODCALLTYPE Release (void)
437   {
438     ULONG ret;
439
440     g_mutex_lock (&m_mutex);
441     m_refcount--;
442     ret = m_refcount;
443     g_mutex_unlock (&m_mutex);
444
445
446     if (ret == 0) {
447       delete this;
448     }
449
450     return ret;
451   }
452
453   virtual HRESULT STDMETHODCALLTYPE
454       VideoInputFormatChanged (BMDVideoInputFormatChangedEvents,
455       IDeckLinkDisplayMode * mode, BMDDetectedVideoInputFormatFlags)
456   {
457     GST_INFO ("Video input format changed");
458
459     g_mutex_lock (&m_input->lock);
460     m_input->input->PauseStreams ();
461     m_input->input->EnableVideoInput (mode->GetDisplayMode (),
462         bmdFormat8BitYUV, bmdVideoInputEnableFormatDetection);
463     m_input->input->FlushStreams ();
464     m_input->input->StartStreams ();
465     m_input->mode =
466         gst_decklink_get_mode (gst_decklink_get_mode_enum_from_bmd
467         (mode->GetDisplayMode ()));
468     g_mutex_unlock (&m_input->lock);
469
470     return S_OK;
471   }
472
473   virtual HRESULT STDMETHODCALLTYPE
474       VideoInputFrameArrived (IDeckLinkVideoInputFrame * video_frame,
475       IDeckLinkAudioInputPacket * audio_packet)
476   {
477     GstElement *videosrc = NULL, *audiosrc = NULL;
478     void (*got_video_frame) (GstElement * videosrc,
479         IDeckLinkVideoInputFrame * frame, GstDecklinkModeEnum mode,
480         GstClockTime capture_time, GstClockTime capture_duration) = NULL;
481     void (*got_audio_packet) (GstElement * videosrc,
482         IDeckLinkAudioInputPacket * packet, GstClockTime capture_time) = NULL;
483     GstDecklinkModeEnum mode;
484     BMDTimeValue capture_time, capture_duration;
485     HRESULT res;
486
487     res =
488         video_frame->GetHardwareReferenceTimestamp (GST_SECOND, &capture_time,
489         &capture_duration);
490     if (res != S_OK) {
491       GST_ERROR ("Failed to get capture time: 0x%08x", res);
492       capture_time = GST_CLOCK_TIME_NONE;
493       capture_duration = GST_CLOCK_TIME_NONE;
494     }
495
496     g_mutex_lock (&m_input->lock);
497
498     if (capture_time > (BMDTimeValue) m_input->clock_start_time)
499       capture_time -= m_input->clock_start_time;
500     else
501       capture_time = 0;
502
503     if (capture_time > (BMDTimeValue) m_input->clock_offset)
504       capture_time -= m_input->clock_offset;
505     else
506       capture_time = 0;
507
508     if (m_input->videosrc) {
509       videosrc = GST_ELEMENT_CAST (gst_object_ref (m_input->videosrc));
510       got_video_frame = m_input->got_video_frame;
511     }
512     mode = gst_decklink_get_mode_enum_from_bmd (m_input->mode->mode);
513
514     if (m_input->audiosrc) {
515       audiosrc = GST_ELEMENT_CAST (gst_object_ref (m_input->audiosrc));
516       got_audio_packet = m_input->got_audio_packet;
517     }
518     g_mutex_unlock (&m_input->lock);
519
520     if (got_video_frame && videosrc) {
521       got_video_frame (videosrc, video_frame, mode, capture_time,
522           capture_duration);
523     }
524
525     if (got_audio_packet && audiosrc) {
526       m_input->got_audio_packet (audiosrc, audio_packet, capture_time);
527     }
528
529     gst_object_replace ((GstObject **) & videosrc, NULL);
530     gst_object_replace ((GstObject **) & audiosrc, NULL);
531
532     return S_OK;
533   }
534 };
535
536 #ifdef _MSC_VER
537 /* FIXME: We currently never deinit this */
538
539 static GMutex com_init_lock;
540 static GMutex com_deinit_lock;
541 static GCond com_init_cond;
542 static GCond com_deinit_cond;
543 static GCond com_deinited_cond;
544 static gboolean com_initialized = FALSE;
545
546 /* COM initialization/uninitialization thread */
547 static gpointer
548 gst_decklink_com_thread (gpointer data)
549 {
550   HRESULT res;
551
552   g_mutex_lock (&com_init_lock);
553
554   /* Initialize COM with a MTA for this process. This thread will
555    * be the first one to enter the apartement and the last one to leave
556    * it, unitializing COM properly */
557
558   res = CoInitializeEx (0, COINIT_MULTITHREADED);
559   if (res == S_FALSE)
560     GST_WARNING ("COM has been already initialized in the same process");
561   else if (res == RPC_E_CHANGED_MODE)
562     GST_WARNING ("The concurrency model of COM has changed.");
563   else
564     GST_INFO ("COM intialized succesfully");
565
566   com_initialized = TRUE;
567
568   /* Signal other threads waiting on this condition that COM was initialized */
569   g_cond_signal (&com_init_cond);
570
571   g_mutex_unlock (&com_init_lock);
572
573   /* Wait until the unitialize condition is met to leave the COM apartement */
574   g_mutex_lock (&com_deinit_lock);
575   g_cond_wait (&com_deinit_cond, &com_deinit_lock);
576
577   CoUninitialize ();
578   GST_INFO ("COM unintialized succesfully");
579   com_initialized = FALSE;
580   g_cond_signal (&com_deinited_cond);
581   g_mutex_unlock (&com_deinit_lock);
582
583   return NULL;
584 }
585 #endif /* _MSC_VER */
586
587 static GOnce devices_once = G_ONCE_INIT;
588 static int n_devices;
589 static Device devices[10];
590
591 static gpointer
592 init_devices (gpointer data)
593 {
594   IDeckLinkIterator *iterator;
595   IDeckLink *decklink = NULL;
596   HRESULT ret;
597   int i;
598
599 #ifdef _MSC_VER
600   // Start COM thread for Windows
601
602   g_mutex_lock (&com_init_lock);
603
604   /* create the COM initialization thread */
605   g_thread_create ((GThreadFunc) gst_decklink_com_thread, NULL, FALSE, NULL);
606
607   /* wait until the COM thread signals that COM has been initialized */
608   g_cond_wait (&com_init_cond, &com_init_lock);
609   g_mutex_unlock (&com_init_lock);
610 #endif /* _MSC_VER */
611
612   iterator = CreateDeckLinkIteratorInstance ();
613   if (iterator == NULL) {
614     GST_ERROR ("no driver");
615     return NULL;
616   }
617
618   i = 0;
619   ret = iterator->Next (&decklink);
620   while (ret == S_OK) {
621     ret = decklink->QueryInterface (IID_IDeckLinkInput,
622         (void **) &devices[i].input.input);
623     if (ret != S_OK) {
624       GST_WARNING ("selected device does not have input interface");
625     } else {
626       devices[i].input.device = decklink;
627       devices[i].input.clock = gst_decklink_clock_new ("GstDecklinkInputClock");
628       GST_DECKLINK_CLOCK_CAST (devices[i].input.clock)->input =
629           &devices[i].input;
630       devices[i].input.
631           input->SetCallback (new GStreamerDecklinkInputCallback (&devices[i].
632               input));
633     }
634
635     ret = decklink->QueryInterface (IID_IDeckLinkOutput,
636         (void **) &devices[i].output.output);
637     if (ret != S_OK) {
638       GST_WARNING ("selected device does not have output interface");
639     } else {
640       devices[i].output.device = decklink;
641       devices[i].output.clock =
642           gst_decklink_clock_new ("GstDecklinkOutputClock");
643       GST_DECKLINK_CLOCK_CAST (devices[i].output.clock)->output =
644           &devices[i].output;
645     }
646
647     ret = decklink->QueryInterface (IID_IDeckLinkConfiguration,
648         (void **) &devices[i].input.config);
649     if (ret != S_OK) {
650       GST_WARNING ("selected device does not have config interface");
651     }
652
653     ret = decklink->QueryInterface (IID_IDeckLinkAttributes,
654         (void **) &devices[i].input.attributes);
655     if (ret != S_OK) {
656       GST_WARNING ("selected device does not have attributes interface");
657     }
658
659     ret = iterator->Next (&decklink);
660     i++;
661
662     if (i == 10) {
663       GST_WARNING ("this hardware has more then 10 devices");
664       break;
665     }
666   }
667
668   n_devices = i;
669
670   iterator->Release ();
671
672   return NULL;
673 }
674
675 GstDecklinkOutput *
676 gst_decklink_acquire_nth_output (gint n, GstElement * sink, gboolean is_audio)
677 {
678   GstDecklinkOutput *output;
679
680   g_once (&devices_once, init_devices, NULL);
681
682   if (n >= n_devices)
683     return NULL;
684
685   output = &devices[n].output;
686   if (!output->output) {
687     GST_ERROR ("Device %d has no output", n);
688     return NULL;
689   }
690
691   g_mutex_lock (&output->lock);
692   if (is_audio && !output->audiosink) {
693     output->audiosink = GST_ELEMENT_CAST (gst_object_ref (sink));
694     g_mutex_unlock (&output->lock);
695     return output;
696   } else if (!output->videosink) {
697     output->videosink = GST_ELEMENT_CAST (gst_object_ref (sink));
698     g_mutex_unlock (&output->lock);
699     return output;
700   }
701   g_mutex_unlock (&output->lock);
702
703   GST_ERROR ("Output device %d (audio: %d) in use already", n, is_audio);
704   return NULL;
705 }
706
707 void
708 gst_decklink_release_nth_output (gint n, GstElement * sink, gboolean is_audio)
709 {
710   GstDecklinkOutput *output;
711
712   if (n >= n_devices)
713     return;
714
715   output = &devices[n].output;
716   g_assert (output->output);
717
718   g_mutex_lock (&output->lock);
719   if (is_audio) {
720     g_assert (output->audiosink == sink);
721     gst_object_unref (sink);
722     output->audiosink = NULL;
723   } else {
724     g_assert (output->videosink == sink);
725     gst_object_unref (sink);
726     output->videosink = NULL;
727   }
728   g_mutex_unlock (&output->lock);
729 }
730
731 void
732 gst_decklink_output_set_audio_clock (GstDecklinkOutput * output,
733     GstClock * clock)
734 {
735   g_mutex_lock (&output->lock);
736   if (output->audio_clock)
737     gst_object_unref (output->audio_clock);
738   output->audio_clock = clock;
739   if (clock)
740     gst_object_ref (clock);
741   g_mutex_unlock (&output->lock);
742 }
743
744
745 GstClock *
746 gst_decklink_output_get_audio_clock (GstDecklinkOutput * output)
747 {
748   GstClock *ret = NULL;
749
750   g_mutex_lock (&output->lock);
751   if (output->audio_clock)
752     ret = GST_CLOCK_CAST (gst_object_ref (output->audio_clock));
753   g_mutex_unlock (&output->lock);
754
755   return ret;
756 }
757
758 GstDecklinkInput *
759 gst_decklink_acquire_nth_input (gint n, GstElement * src, gboolean is_audio)
760 {
761   GstDecklinkInput *input;
762
763   g_once (&devices_once, init_devices, NULL);
764
765   if (n >= n_devices)
766     return NULL;
767
768   input = &devices[n].input;
769   if (!input->input) {
770     GST_ERROR ("Device %d has no input", n);
771     return NULL;
772   }
773
774   g_mutex_lock (&input->lock);
775   if (is_audio && !input->audiosrc) {
776     input->audiosrc = GST_ELEMENT_CAST (gst_object_ref (src));
777     g_mutex_unlock (&input->lock);
778     return input;
779   } else if (!input->videosrc) {
780     input->videosrc = GST_ELEMENT_CAST (gst_object_ref (src));
781     g_mutex_unlock (&input->lock);
782     return input;
783   }
784   g_mutex_unlock (&input->lock);
785
786   GST_ERROR ("Input device %d (audio: %d) in use already", n, is_audio);
787   return NULL;
788 }
789
790 void
791 gst_decklink_release_nth_input (gint n, GstElement * src, gboolean is_audio)
792 {
793   GstDecklinkInput *input;
794
795   if (n >= n_devices)
796     return;
797
798   input = &devices[n].input;
799   g_assert (input->input);
800
801   g_mutex_lock (&input->lock);
802   if (is_audio) {
803     g_assert (input->audiosrc == src);
804     gst_object_unref (src);
805     input->audiosrc = NULL;
806   } else {
807     g_assert (input->videosrc == src);
808     gst_object_unref (src);
809     input->videosrc = NULL;
810   }
811   g_mutex_unlock (&input->lock);
812 }
813
814 G_DEFINE_TYPE (GstDecklinkClock, gst_decklink_clock, GST_TYPE_SYSTEM_CLOCK);
815
816 static GstClockTime gst_decklink_clock_get_internal_time (GstClock * clock);
817
818 static void
819 gst_decklink_clock_class_init (GstDecklinkClockClass * klass)
820 {
821   GstClockClass *clock_class = (GstClockClass *) klass;
822
823   clock_class->get_internal_time = gst_decklink_clock_get_internal_time;
824 }
825
826 static void
827 gst_decklink_clock_init (GstDecklinkClock * clock)
828 {
829   GST_OBJECT_FLAG_SET (clock, GST_CLOCK_FLAG_CAN_SET_MASTER);
830 }
831
832 static GstClock *
833 gst_decklink_clock_new (const gchar * name)
834 {
835   GstDecklinkClock *self =
836       GST_DECKLINK_CLOCK (g_object_new (GST_TYPE_DECKLINK_CLOCK, "name", name,
837           "clock-type", GST_CLOCK_TYPE_OTHER, NULL));
838
839   return GST_CLOCK_CAST (self);
840 }
841
842 static GstClockTime
843 gst_decklink_clock_get_internal_time (GstClock * clock)
844 {
845   GstDecklinkClock *self = GST_DECKLINK_CLOCK (clock);
846   GstClockTime result, start_time, last_time;
847   GstClockTimeDiff offset;
848   BMDTimeValue time;
849   HRESULT ret;
850
851   if (self->input != NULL) {
852     g_mutex_lock (&self->input->lock);
853     start_time = self->input->clock_start_time;
854     offset = self->input->clock_offset;
855     last_time = self->input->clock_last_time;
856     time = -1;
857     if (!self->input->started) {
858       result = last_time;
859       ret = -1;
860     } else {
861       ret =
862           self->input->input->GetHardwareReferenceClock (GST_SECOND, &time,
863           NULL, NULL);
864       if (ret == S_OK && time >= 0) {
865         result = time;
866         if (start_time == GST_CLOCK_TIME_NONE)
867           start_time = self->input->clock_start_time = result;
868
869         if (result > start_time)
870           result -= start_time;
871         else
872           result = 0;
873
874         if (self->input->clock_restart) {
875           self->input->clock_offset = result - last_time;
876           offset = self->input->clock_offset;
877           self->input->clock_restart = FALSE;
878         }
879         result = MAX (last_time, result);
880         result -= offset;
881         result = MAX (last_time, result);
882       } else {
883         result = last_time;
884       }
885
886       self->input->clock_last_time = result;
887     }
888     g_mutex_unlock (&self->input->lock);
889   } else if (self->output != NULL) {
890     g_mutex_lock (&self->output->lock);
891     start_time = self->output->clock_start_time;
892     offset = self->output->clock_offset;
893     last_time = self->output->clock_last_time;
894     time = -1;
895     if (!self->output->started) {
896       result = last_time;
897       ret = -1;
898     } else {
899       ret =
900           self->output->output->GetHardwareReferenceClock (GST_SECOND, &time,
901           NULL, NULL);
902       if (ret == S_OK && time >= 0) {
903         result = time;
904
905         if (start_time == GST_CLOCK_TIME_NONE)
906           start_time = self->output->clock_start_time = result;
907
908         if (result > start_time)
909           result -= start_time;
910         else
911           result = 0;
912
913         if (self->output->clock_restart) {
914           self->output->clock_offset = result - last_time;
915           offset = self->output->clock_offset;
916           self->output->clock_restart = FALSE;
917         }
918         result = MAX (last_time, result);
919         result -= offset;
920         result = MAX (last_time, result);
921       } else {
922         result = last_time;
923       }
924
925       self->output->clock_last_time = result;
926     }
927     g_mutex_unlock (&self->output->lock);
928   } else {
929     g_assert_not_reached ();
930   }
931   GST_LOG_OBJECT (clock,
932       "result %" GST_TIME_FORMAT " time %" GST_TIME_FORMAT " last time %"
933       GST_TIME_FORMAT " offset %" GST_TIME_FORMAT " start time %"
934       GST_TIME_FORMAT " (ret: 0x%08x)", GST_TIME_ARGS (result),
935       GST_TIME_ARGS (time), GST_TIME_ARGS (last_time), GST_TIME_ARGS (offset),
936       GST_TIME_ARGS (start_time), ret);
937
938   return result;
939 }
940
941 static gboolean
942 plugin_init (GstPlugin * plugin)
943 {
944   GST_DEBUG_CATEGORY_INIT (gst_decklink_debug, "decklink", 0,
945       "debug category for decklink plugin");
946
947   gst_element_register (plugin, "decklinkaudiosink", GST_RANK_NONE,
948       GST_TYPE_DECKLINK_AUDIO_SINK);
949   gst_element_register (plugin, "decklinkvideosink", GST_RANK_NONE,
950       GST_TYPE_DECKLINK_VIDEO_SINK);
951   gst_element_register (plugin, "decklinkaudiosrc", GST_RANK_NONE,
952       GST_TYPE_DECKLINK_AUDIO_SRC);
953   gst_element_register (plugin, "decklinkvideosrc", GST_RANK_NONE,
954       GST_TYPE_DECKLINK_VIDEO_SRC);
955   return TRUE;
956 }
957
958 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
959     GST_VERSION_MINOR,
960     decklink,
961     "Blackmagic Decklink plugin",
962     plugin_init, VERSION, "LGPL", PACKAGE_NAME, GST_PACKAGE_ORIGIN)