]> git.sesse.net Git - mlt/blob - src/modules/decklink/consumer_decklink.cpp
fix decklink build on Windows
[mlt] / src / modules / decklink / consumer_decklink.cpp
1 /*
2  * consumer_decklink.c -- output through Blackmagic Design DeckLink
3  * Copyright (C) 2010 Dan Dennedy <dan@dennedy.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with consumer library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #define __STDC_FORMAT_MACROS  /* see inttypes.h */
21 #include <framework/mlt.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <pthread.h>
25 #include <unistd.h>
26 #include <sys/time.h>
27 #include <limits.h>
28 #include <pthread.h>
29 #ifdef WIN32
30 #include <objbase.h>
31 #include "DeckLinkAPI_h.h"
32 #else
33 #include "DeckLinkAPI.h"
34 typedef const char* BSTR;
35 #endif
36
37 #define SAFE_RELEASE(V) if (V) { V->Release(); V = NULL; }
38
39 static const unsigned PREROLL_MINIMUM = 3;
40
41 class DeckLinkConsumer
42         : public IDeckLinkVideoOutputCallback
43 {
44 private:
45         mlt_consumer_s              m_consumer;
46         IDeckLink*                  m_deckLink;
47         IDeckLinkOutput*            m_deckLinkOutput;
48         IDeckLinkDisplayMode*       m_displayMode;
49         int                         m_width;
50         int                         m_height;
51         BMDTimeValue                m_duration;
52         BMDTimeScale                m_timescale;
53         double                      m_fps;
54         uint64_t                    m_count;
55         int                         m_channels;
56         unsigned                    m_dropped;
57         IDeckLinkMutableVideoFrame* m_decklinkFrame;
58         bool                        m_isAudio;
59         int                         m_isKeyer;
60         IDeckLinkKeyer*             m_deckLinkKeyer;
61         bool                        m_terminate_on_pause;
62         uint32_t                    m_preroll;
63         uint32_t                    m_acnt;
64         bool                        m_reprio;
65         pthread_t                   m_prerollThread;
66
67         IDeckLinkDisplayMode* getDisplayMode()
68         {
69                 mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( getConsumer() ) );
70                 IDeckLinkDisplayModeIterator* iter = NULL;
71                 IDeckLinkDisplayMode* mode = NULL;
72                 IDeckLinkDisplayMode* result = 0;
73                 
74                 if ( m_deckLinkOutput->GetDisplayModeIterator( &iter ) == S_OK )
75                 {
76                         while ( !result && iter->Next( &mode ) == S_OK )
77                         {
78                                 m_width = mode->GetWidth();
79                                 m_height = mode->GetHeight();
80                                 mode->GetFrameRate( &m_duration, &m_timescale );
81                                 m_fps = (double) m_timescale / m_duration;
82                                 int p = mode->GetFieldDominance() == bmdProgressiveFrame;
83                                 mlt_log_verbose( getConsumer(), "BMD mode %dx%d %.3f fps prog %d\n", m_width, m_height, m_fps, p );
84                                 
85                                 if ( m_width == profile->width && p == profile->progressive
86                                          && m_fps == mlt_profile_fps( profile )
87                                          && ( m_height == profile->height || ( m_height == 486 && profile->height == 480 ) ) )
88                                         result = mode;
89                                 else
90                                         SAFE_RELEASE( mode );
91                         }
92                         SAFE_RELEASE( iter );
93                 }
94                 
95                 return result;
96         }
97         
98 public:
99         mlt_consumer getConsumer()
100                 { return &m_consumer; }
101
102         DeckLinkConsumer()
103         {
104                 m_displayMode = NULL;
105                 m_deckLinkKeyer = NULL;
106                 m_deckLinkOutput = NULL;
107                 m_deckLink = NULL;
108         }
109
110         ~DeckLinkConsumer()
111         {
112                 SAFE_RELEASE( m_displayMode );
113                 SAFE_RELEASE( m_deckLinkKeyer );
114                 SAFE_RELEASE( m_deckLinkOutput );
115                 SAFE_RELEASE( m_deckLink );
116         }
117         
118         bool open( unsigned card = 0 )
119         {
120                 unsigned i = 0;
121 #ifdef WIN32
122                 IDeckLinkIterator* deckLinkIterator = NULL;
123                 HRESULT result =  CoInitialize( NULL );
124                 if ( FAILED( result ) )
125                 {
126                         mlt_log_error( getConsumer(), "COM initialization failed\n" );
127                         return false;
128                 }
129                 result = CoCreateInstance( CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**) &deckLinkIterator );
130                 if ( FAILED( result ) )
131                 {
132                         mlt_log_error( getConsumer(), "The DeckLink drivers not installed.\n" );
133                         return false;
134                 }
135 #else
136                 IDeckLinkIterator* deckLinkIterator = CreateDeckLinkIteratorInstance();
137                 
138                 if ( !deckLinkIterator )
139                 {
140                         mlt_log_error( getConsumer(), "The DeckLink drivers not installed.\n" );
141                         return false;
142                 }
143 #endif
144
145                 // Connect to the Nth DeckLink instance
146                 for ( i = 0; deckLinkIterator->Next( &m_deckLink ) == S_OK ; i++)
147                 {
148                         if( i == card )
149                                 break;
150                         else
151                                 SAFE_RELEASE( m_deckLink );
152                 }
153                 SAFE_RELEASE( deckLinkIterator );
154                 if ( !m_deckLink )
155                 {
156                         mlt_log_error( getConsumer(), "DeckLink card not found\n" );
157                         return false;
158                 }
159
160                 // Obtain the audio/video output interface (IDeckLinkOutput)
161                 if ( m_deckLink->QueryInterface( IID_IDeckLinkOutput, (void**)&m_deckLinkOutput ) != S_OK )
162                 {
163                         mlt_log_error( getConsumer(), "No DeckLink cards support output\n" );
164                         SAFE_RELEASE( m_deckLink );
165                         return false;
166                 }
167                 
168                 // Get the keyer interface
169                 IDeckLinkAttributes *deckLinkAttributes = 0;
170                 if ( m_deckLink->QueryInterface( IID_IDeckLinkAttributes, (void**) &deckLinkAttributes ) == S_OK )
171                 {
172 #ifdef WIN32
173                         BOOL flag = FALSE;
174 #else
175                         bool flag = false;
176 #endif
177                         if ( deckLinkAttributes->GetFlag( BMDDeckLinkSupportsInternalKeying, &flag ) == S_OK && flag )
178                         {
179                                 if ( m_deckLink->QueryInterface( IID_IDeckLinkKeyer, (void**) &m_deckLinkKeyer ) != S_OK )
180                                 {
181                                         mlt_log_error( getConsumer(), "Failed to get keyer\n" );
182                                         SAFE_RELEASE( m_deckLinkOutput );
183                                         SAFE_RELEASE( m_deckLink );
184                                         return false;
185                                 }
186                         }
187                         SAFE_RELEASE( deckLinkAttributes );
188                 }
189
190                 // Provide this class as a delegate to the audio and video output interfaces
191                 m_deckLinkOutput->SetScheduledFrameCompletionCallback( this );
192                 
193                 return true;
194         }
195
196
197         void* preroll_thread()
198         {
199                 // preroll frames
200                 for ( unsigned i = 0; i < m_preroll; i++ )
201                         ScheduleNextFrame( true );
202
203                 // start scheduled playback
204                 m_deckLinkOutput->StartScheduledPlayback( 0, m_timescale, 1.0 );
205
206                 return 0;
207         }
208
209         static void* preroll_thread_proxy( void* arg )
210         {
211                 DeckLinkConsumer* self = static_cast< DeckLinkConsumer* >( arg );
212                 return self->preroll_thread();
213         }
214
215         bool start( unsigned preroll )
216         {
217                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
218
219                 // Initialize members
220                 m_count = 0;
221                 m_dropped = 0;
222                 m_decklinkFrame = NULL;
223                 preroll = preroll < PREROLL_MINIMUM ? PREROLL_MINIMUM : preroll;
224                 m_channels = mlt_properties_get_int( properties, "channels" );
225                 m_isAudio = !mlt_properties_get_int( properties, "audio_off" );
226                 m_terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" );
227
228
229                 m_displayMode = getDisplayMode();
230                 if ( !m_displayMode )
231                 {
232                         mlt_log_error( getConsumer(), "Profile is not compatible with decklink.\n" );
233                         return false;
234                 }
235                 
236                 // Set the keyer
237                 if ( m_deckLinkKeyer && ( m_isKeyer = mlt_properties_get_int( properties, "keyer" ) ) )
238                 {
239                         bool external = ( m_isKeyer == 2 );
240                         double level = mlt_properties_get_double( properties, "keyer_level" );
241
242                         if ( m_deckLinkKeyer->Enable( external ) != S_OK )
243                                 mlt_log_error( getConsumer(), "Failed to enable %s keyer\n",
244                                         external ? "external" : "internal" );
245                         m_deckLinkKeyer->SetLevel( level <= 1 ? ( level > 0 ? 255 * level : 255 ) : 255 );
246                 }
247                 else if ( m_deckLinkKeyer )
248                 {
249                         m_deckLinkKeyer->Disable();
250                 }
251
252                 // Set the video output mode
253                 if ( S_OK != m_deckLinkOutput->EnableVideoOutput( m_displayMode->GetDisplayMode(), bmdVideoOutputFlagDefault ) )
254                 {
255                         mlt_log_error( getConsumer(), "Failed to enable video output\n" );
256                         return false;
257                 }
258
259                 // Set the audio output mode
260                 if ( !m_isAudio )
261                 {
262                         m_deckLinkOutput->DisableAudioOutput();
263                         return true;
264                 }
265                 if ( S_OK != m_deckLinkOutput->EnableAudioOutput( bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger,
266                         m_channels, bmdAudioOutputStreamTimestamped ) )
267                 {
268                         mlt_log_error( getConsumer(), "Failed to enable audio output\n" );
269                         stop();
270                         return false;
271                 }
272
273                 m_preroll = preroll;
274                 m_reprio = false;
275
276                 // Do preroll in thread to ensure asynchronicity of mlt_consumer_start().
277                 pthread_create( &m_prerollThread, NULL, preroll_thread_proxy, this );
278
279                 // Set the running state
280                 mlt_properties_set_int( properties, "running", 1 );
281
282                 return true;
283         }
284         
285         bool stop()
286         {
287                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
288                 bool wasRunning = !!mlt_properties_get_int( properties, "running" );
289
290                 // set running state is 0
291                 mlt_properties_set_int( properties, "running", 0 );
292                 mlt_consumer_stopped( getConsumer() );
293
294                 // Stop the audio and video output streams immediately
295                 if ( m_deckLinkOutput )
296                 {
297                         m_deckLinkOutput->StopScheduledPlayback( 0, 0, 0 );
298                         m_deckLinkOutput->DisableAudioOutput();
299                         m_deckLinkOutput->DisableVideoOutput();
300                 }
301
302                 // release decklink frame
303                 SAFE_RELEASE( m_decklinkFrame );
304
305                 if ( wasRunning )
306                         pthread_join( m_prerollThread, NULL );
307
308                 return true;
309         }
310
311         void renderAudio( mlt_frame frame )
312         {
313                 mlt_audio_format format = mlt_audio_s16;
314                 int frequency = bmdAudioSampleRate48kHz;
315                 int samples = mlt_sample_calculator( m_fps, frequency, m_count );
316                 int16_t *pcm = 0;
317
318                 if ( !mlt_frame_get_audio( frame, (void**) &pcm, &format, &frequency, &m_channels, &samples ) )
319                 {
320 #ifdef WIN32
321                         unsigned long written = 0;
322 #else
323                         uint32_t written = 0;
324 #endif
325                         BMDTimeValue streamTime = m_count * frequency * m_duration / m_timescale;
326                         m_deckLinkOutput->GetBufferedAudioSampleFrameCount( &written );
327                         if ( written > (m_preroll + 1) * samples )
328                         {
329                                 mlt_log_verbose( getConsumer(), "renderAudio: will flush %lu audiosamples\n", written );
330                                 m_deckLinkOutput->FlushBufferedAudioSamples();
331                         };
332 #ifdef WIN32
333                         m_deckLinkOutput->ScheduleAudioSamples( pcm, samples, streamTime, frequency, (unsigned long*) &written );
334 #else
335                         m_deckLinkOutput->ScheduleAudioSamples( pcm, samples, streamTime, frequency, &written );
336 #endif
337
338                         if ( written != (uint32_t) samples )
339                                 mlt_log_verbose( getConsumer(), "renderAudio: samples=%d, written=%lu\n", samples, written );
340                 }
341         }
342
343         bool createFrame( IDeckLinkMutableVideoFrame** decklinkFrame )
344         {
345                 BMDPixelFormat format = m_isKeyer? bmdFormat8BitARGB : bmdFormat8BitYUV;
346                 IDeckLinkMutableVideoFrame* frame = 0;
347                 uint8_t *buffer = 0;
348                 int stride = m_width * ( m_isKeyer? 4 : 2 );
349
350                 *decklinkFrame = NULL;
351
352                 // Generate a DeckLink video frame
353                 if ( S_OK != m_deckLinkOutput->CreateVideoFrame( m_width, m_height,
354                         stride, format, bmdFrameFlagDefault, &frame ) )
355                 {
356                         mlt_log_verbose( getConsumer(), "Failed to create video frame\n" );
357                         stop();
358                         return false;
359                 }
360                 
361                 // Make the first line black for field order correction.
362                 if ( S_OK == frame->GetBytes( (void**) &buffer ) && buffer )
363                 {
364                         if ( m_isKeyer )
365                         {
366                                 memset( buffer, 0, stride );
367                         }
368                         else for ( int i = 0; i < m_width; i++ )
369                         {
370                                 *buffer++ = 128;
371                                 *buffer++ = 16;
372                         }
373                 }
374
375                 *decklinkFrame = frame;
376
377                 return true;
378         }
379
380         void renderVideo( mlt_frame frame )
381         {
382                 mlt_image_format format = m_isKeyer? mlt_image_rgb24a : mlt_image_yuv422;
383                 uint8_t* image = 0;
384                 int rendered = mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "rendered");
385                 int height = m_height;
386
387                 if ( rendered && !mlt_frame_get_image( frame, &image, &format, &m_width, &height, 0 ) )
388                 {
389                         uint8_t* buffer = 0;
390                         int stride = m_width * ( m_isKeyer? 4 : 2 );
391
392                         SAFE_RELEASE( m_decklinkFrame );
393                         if ( createFrame( &m_decklinkFrame ) )
394                                 m_decklinkFrame->GetBytes( (void**) &buffer );
395
396                         if ( buffer )
397                         {
398                                 int progressive = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "progressive" );
399
400                                 // NTSC SDI is always 486 lines
401                                 if ( m_height == 486 && height == 480 )
402                                 {
403                                         // blank first 6 lines
404                                         if ( m_isKeyer )
405                                         {
406                                                 memset( buffer, 0, stride * 6 );
407                                                 buffer += stride * 6;
408                                         }
409                                         else for ( int i = 0; i < m_width * 6; i++ )
410                                         {
411                                                 *buffer++ = 128;
412                                                 *buffer++ = 16;
413                                         }
414                                 }
415                                 if ( !m_isKeyer )
416                                 {
417                                         // Normal non-keyer playout - needs byte swapping
418                                         if ( !progressive && m_displayMode->GetFieldDominance() == bmdUpperFieldFirst )
419                                                 // convert lower field first to top field first
420                                                 swab( (char*) image, (char*) buffer + stride, stride * ( height - 1 ) );
421                                         else
422                                                 swab( (char*) image, (char*) buffer, stride * height );
423                                 }
424                                 else if ( !mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "test_image" ) )
425                                 {
426                                         // Normal keyer output
427                                         int y = height + 1;
428                                         uint32_t* s = (uint32_t*) image;
429                                         uint32_t* d = (uint32_t*) buffer;
430
431                                         if ( !progressive && m_displayMode->GetFieldDominance() == bmdUpperFieldFirst )
432                                         {
433                                                 // Correct field order
434                                                 height--;
435                                                 y--;
436                                                 d += m_width;
437                                         }
438
439                                         // Need to relocate alpha channel RGBA => ARGB
440                                         while ( --y )
441                                         {
442                                                 int x = m_width + 1;
443                                                 while ( --x )
444                                                 {
445                                                         *d++ = ( *s << 8 ) | ( *s >> 24 );
446                                                         s++;
447                                                 }
448                                         }
449                                 }
450                                 else
451                                 {
452                                         // Keying blank frames - nullify alpha
453                                         memset( buffer, 0, stride * height );
454                                 }
455                         }
456                 }
457                 if ( m_decklinkFrame )
458                         m_deckLinkOutput->ScheduleVideoFrame( m_decklinkFrame, m_count * m_duration, m_duration, m_timescale );
459
460                 if ( !rendered )
461                         mlt_log_verbose( getConsumer(), "dropped video frame %u\n", ++m_dropped );
462         }
463
464         HRESULT render( mlt_frame frame )
465         {
466                 HRESULT result = S_OK;
467
468                 // Get the audio
469                 double speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES(frame), "_speed" );
470                 if ( m_isAudio && speed == 1.0 )
471                         renderAudio( frame );
472
473                 // Get the video
474                 renderVideo( frame );
475                 ++m_count;
476
477                 return result;
478         }
479         
480         // *** DeckLink API implementation of IDeckLinkVideoOutputCallback IDeckLinkAudioOutputCallback *** //
481
482         // IUnknown needs only a dummy implementation
483         virtual HRESULT STDMETHODCALLTYPE QueryInterface( REFIID iid, LPVOID *ppv )
484                 { return E_NOINTERFACE; }
485         virtual ULONG STDMETHODCALLTYPE AddRef()
486                 { return 1; }
487         virtual ULONG STDMETHODCALLTYPE Release()
488                 { return 1; }
489         
490         /************************* DeckLink API Delegate Methods *****************************/
491         
492         virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted( IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult completed )
493         {
494                 if( !m_reprio )
495                 {
496                         mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
497
498                         if ( mlt_properties_get( properties, "priority" ) )
499                         {
500                                 int r;
501                                 pthread_t thread;
502                                 pthread_attr_t tattr;
503                                 struct sched_param param;
504
505                                 pthread_attr_init(&tattr);
506                                 pthread_attr_setschedpolicy(&tattr, SCHED_FIFO);
507
508                                 if ( !strcmp( "max", mlt_properties_get( properties, "priority" ) ) )
509                                         param.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1;
510                                 else if ( !strcmp( "min", mlt_properties_get( properties, "priority" ) ) )
511                                         param.sched_priority = sched_get_priority_min(SCHED_FIFO) + 1;
512                                 else
513                                         param.sched_priority = mlt_properties_get_int( properties, "priority" );
514
515                                 pthread_attr_setschedparam(&tattr, &param);
516
517                                 thread = pthread_self();
518
519                                 r = pthread_setschedparam(thread, SCHED_FIFO, &param);
520                                 if( r )
521                                         mlt_log_verbose( getConsumer(),
522                                                 "ScheduledFrameCompleted: pthread_setschedparam returned %d\n", r);
523                                 else
524                                         mlt_log_verbose( getConsumer(),
525                                                 "ScheduledFrameCompleted: param.sched_priority=%d\n", param.sched_priority);
526                         };
527
528                         m_reprio = true;
529                 };
530
531 #ifdef WIN32
532                 unsigned long cnt;
533 #else
534                 uint32_t cnt;
535 #endif
536                 m_deckLinkOutput->GetBufferedAudioSampleFrameCount( &cnt );
537                 if ( cnt != m_acnt )
538                 {
539                         mlt_log_debug( getConsumer(),
540                                 "ScheduledFrameCompleted: GetBufferedAudioSampleFrameCount %u -> %lu, m_count=%"PRIu64"\n",
541                                 m_acnt, cnt, m_count );
542                         m_acnt = cnt;
543                 }
544
545                 // When a video frame has been released by the API, schedule another video frame to be output
546
547                 // ignore handler if frame was flushed
548                 if ( bmdOutputFrameFlushed == completed )
549                         return S_OK;
550
551                 // schedule next frame
552                 ScheduleNextFrame( false );
553
554                 // step forward frames counter if underrun
555                 if ( bmdOutputFrameDisplayedLate == completed )
556                 {
557                         mlt_log_verbose( getConsumer(), "ScheduledFrameCompleted: bmdOutputFrameDisplayedLate == completed\n" );
558                         m_count++;
559                 }
560                 if ( bmdOutputFrameDropped == completed )
561                 {
562                         mlt_log_verbose( getConsumer(), "ScheduledFrameCompleted: bmdOutputFrameDropped == completed\n" );
563                         m_count++;
564                 }
565
566                 return S_OK;
567         }
568
569         virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
570         {
571                 return mlt_consumer_is_stopped( getConsumer() ) ? S_FALSE : S_OK;
572         }
573         
574
575         void ScheduleNextFrame( bool preroll )
576         {
577                 // get the consumer
578                 mlt_consumer consumer = getConsumer();
579
580                 // Get the properties
581                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
582
583                 // Frame and size
584                 mlt_frame frame = NULL;
585
586                 if( mlt_properties_get_int( properties, "running" ) || preroll )
587                 {
588                         frame = mlt_consumer_rt_frame( consumer );
589                         if ( frame != NULL )
590                         {
591                                 render( frame );
592
593                                 mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
594
595                                 // terminate on pause
596                                 if ( m_terminate_on_pause &&
597                                         mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0 )
598                                         stop();
599
600                                 mlt_frame_close( frame );
601                         }
602                 }
603         }
604 };
605
606 /** Start the consumer.
607  */
608
609 static int start( mlt_consumer consumer )
610 {
611         // Get the properties
612         mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
613         DeckLinkConsumer* decklink = (DeckLinkConsumer*) consumer->child;
614         return decklink->start( mlt_properties_get_int( properties, "preroll" ) ) ? 0 : 1;
615 }
616
617 /** Stop the consumer.
618  */
619
620 static int stop( mlt_consumer consumer )
621 {
622         // Get the properties
623         DeckLinkConsumer* decklink = (DeckLinkConsumer*) consumer->child;
624         return decklink->stop();
625 }
626
627 /** Determine if the consumer is stopped.
628  */
629
630 static int is_stopped( mlt_consumer consumer )
631 {
632         // Get the properties
633         mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
634         return !mlt_properties_get_int( properties, "running" );
635 }
636
637 /** Close the consumer.
638  */
639
640 static void close( mlt_consumer consumer )
641 {
642         // Stop the consumer
643         mlt_consumer_stop( consumer );
644
645         // Close the parent
646         consumer->close = NULL;
647         mlt_consumer_close( consumer );
648
649         // Free the memory
650         delete (DeckLinkConsumer*) consumer->child;
651 }
652
653 extern "C" {
654
655 // Listen for the list_devices property to be set
656 static void on_property_changed( void*, mlt_properties properties, const char *name )
657 {
658         IDeckLinkIterator* decklinkIterator = NULL;
659         IDeckLink* decklink = NULL;
660         IDeckLinkInput* decklinkOutput = NULL;
661         int i = 0;
662
663         if ( name && !strcmp( name, "list_devices" ) )
664                 mlt_event_block( (mlt_event) mlt_properties_get_data( properties, "list-devices-event", NULL ) );
665         else
666                 return;
667
668 #ifdef WIN32
669         if ( FAILED( CoInitialize( NULL ) ) )
670                 return;
671         if ( FAILED( CoCreateInstance( CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**) &decklinkIterator ) ) )
672                 return;
673 #else
674         if ( !( decklinkIterator = CreateDeckLinkIteratorInstance() ) )
675                 return;
676 #endif
677         for ( ; decklinkIterator->Next( &decklink ) == S_OK; i++ )
678         {
679                 if ( decklink->QueryInterface( IID_IDeckLinkOutput, (void**) &decklinkOutput ) == S_OK )
680                 {
681                         char *name = NULL;
682                         if ( decklink->GetModelName( (BSTR*) &name ) == S_OK )
683                         {
684                                 const char *format = "device.%d";
685                                 char *key = (char*) calloc( 1, strlen( format ) + 1 );
686
687                                 sprintf( key, format, i );
688                                 mlt_properties_set( properties, key, name );
689                                 free( key );
690                                 free( name );
691                         }
692                         SAFE_RELEASE( decklinkOutput );
693                 }
694                 SAFE_RELEASE( decklink );
695         }
696         SAFE_RELEASE( decklinkIterator );
697         mlt_properties_set_int( properties, "devices", i );
698 }
699
700 /** Initialise the consumer.
701  */
702
703 mlt_consumer consumer_decklink_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
704 {
705         // Allocate the consumer
706         DeckLinkConsumer* decklink = new DeckLinkConsumer();
707         mlt_consumer consumer = NULL;
708
709         // If allocated
710         if ( decklink && !mlt_consumer_init( decklink->getConsumer(), decklink, profile ) )
711         {
712                 // If initialises without error
713                 if ( decklink->open( arg? atoi(arg) : 0 ) )
714                 {
715                         consumer = decklink->getConsumer();
716                         mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
717
718                         // Setup callbacks
719                         consumer->close = close;
720                         consumer->start = start;
721                         consumer->stop = stop;
722                         consumer->is_stopped = is_stopped;
723                         mlt_properties_set( properties, "deinterlace_method", "onefield" );
724
725                         mlt_event event = mlt_events_listen( properties, properties, "property-changed", (mlt_listener) on_property_changed );
726                         mlt_properties_set_data( properties, "list-devices-event", event, 0, NULL, NULL );
727                 }
728         }
729
730         // Return consumer
731         return consumer;
732 }
733
734 extern mlt_producer producer_decklink_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
735
736 static mlt_properties metadata( mlt_service_type type, const char *id, void *data )
737 {
738         char file[ PATH_MAX ];
739         const char *service_type = NULL;
740         switch ( type )
741         {
742                 case consumer_type:
743                         service_type = "consumer";
744                         break;
745                 case producer_type:
746                         service_type = "producer";
747                         break;
748                 default:
749                         return NULL;
750         }
751         snprintf( file, PATH_MAX, "%s/decklink/%s_%s.yml", mlt_environment( "MLT_DATA" ), service_type, id );
752         return mlt_properties_parse_yaml( file );
753 }
754
755 MLT_REPOSITORY
756 {
757         MLT_REGISTER( consumer_type, "decklink", consumer_decklink_init );
758         MLT_REGISTER( producer_type, "decklink", producer_decklink_init );
759         MLT_REGISTER_METADATA( consumer_type, "decklink", metadata, NULL );
760         MLT_REGISTER_METADATA( producer_type, "decklink", metadata, NULL );
761 }
762
763 } // extern C