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