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