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