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