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