]> git.sesse.net Git - mlt/blob - src/modules/decklink/producer_decklink.cpp
log profile info when profile not compatible with decklink mode
[mlt] / src / modules / decklink / producer_decklink.cpp
1 /*
2  * producer_decklink.c -- input from Blackmagic Design DeckLink
3  * Copyright (C) 2011 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 #include <framework/mlt.h>
21 #include <stdlib.h>
22 #include <pthread.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <limits.h>
26 #include <sys/time.h>
27 #ifdef WIN32
28 #include <objbase.h>
29 #include "DeckLinkAPI_h.h"
30 #else
31 #include "DeckLinkAPI.h"
32 #endif
33
34 class DeckLinkProducer
35         : public IDeckLinkInputCallback
36 {
37 private:
38         mlt_producer     m_producer;
39         IDeckLink*       m_decklink;
40         IDeckLinkInput*  m_decklinkInput;
41         mlt_deque        m_queue;
42         pthread_mutex_t  m_mutex;
43         pthread_cond_t   m_condition;
44         bool             m_started;
45         int              m_dropped;
46         bool             m_isBuffering;
47         int              m_topFieldFirst;
48         int              m_colorspace;
49         int              m_vancLines;
50
51         BMDDisplayMode getDisplayMode( mlt_profile profile, int vancLines )
52         {
53                 IDeckLinkDisplayModeIterator* iter;
54                 IDeckLinkDisplayMode* mode;
55                 BMDDisplayMode result = (BMDDisplayMode) bmdDisplayModeNotSupported;
56
57                 if ( m_decklinkInput->GetDisplayModeIterator( &iter ) == S_OK )
58                 {
59                         while ( !result && iter->Next( &mode ) == S_OK )
60                         {
61                                 int width = mode->GetWidth();
62                                 int height = mode->GetHeight();
63                                 BMDTimeValue duration;
64                                 BMDTimeScale timescale;
65                                 mode->GetFrameRate( &duration, &timescale );
66                                 double fps = (double) timescale / duration;
67                                 int p = mode->GetFieldDominance() == bmdProgressiveFrame;
68                                 m_topFieldFirst = mode->GetFieldDominance() == bmdUpperFieldFirst;
69                                 m_colorspace = ( mode->GetFlags() & bmdDisplayModeColorspaceRec709 ) ? 709 : 601;
70                                 mlt_log_verbose( getProducer(), "BMD mode %dx%d %.3f fps prog %d tff %d\n", width, height, fps, p, m_topFieldFirst );
71
72                                 if ( width == profile->width && p == profile->progressive
73                                          && ( height + vancLines == profile->height || ( height == 486 && profile->height == 480 + vancLines ) )
74                                          && fps == mlt_profile_fps( profile ) )
75                                         result = mode->GetDisplayMode();
76                         }
77                 }
78
79                 return result;
80         }
81
82 public:
83
84         void setProducer( mlt_producer producer )
85                 { m_producer = producer; }
86
87         mlt_producer getProducer() const
88                 { return m_producer; }
89
90         ~DeckLinkProducer()
91         {
92                 if ( m_decklinkInput )
93                         m_decklinkInput->Release();
94                 if ( m_decklink )
95                         m_decklink->Release();
96                 if ( m_queue )
97                 {
98                         stop();
99                         mlt_deque_close( m_queue );
100                         pthread_mutex_destroy( &m_mutex );
101                         pthread_cond_destroy( &m_condition );
102                 }
103         }
104
105         bool open( unsigned card =  0 )
106         {
107                 IDeckLinkIterator* decklinkIterator = NULL;
108                 try
109                 {
110 #ifdef WIN32
111                         HRESULT result =  CoInitialize( NULL );
112                         if ( FAILED( result ) )
113                                 throw "COM initialization failed";
114                         result = CoCreateInstance( CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**) &decklinkIterator );
115                         if ( FAILED( result ) )
116                                 throw "The DeckLink drivers are not installed.";
117 #else
118                         decklinkIterator = CreateDeckLinkIteratorInstance();
119                         if ( !decklinkIterator )
120                                 throw "The DeckLink drivers are not installed.";
121 #endif
122
123                         // Connect to the Nth DeckLink instance
124                         unsigned i = 0;
125                         do {
126                                 if ( decklinkIterator->Next( &m_decklink ) != S_OK )
127                                         throw "DeckLink card not found.";
128                         } while ( ++i <= card );
129                         decklinkIterator->Release();
130
131                         // Get the input interface
132                         if ( m_decklink->QueryInterface( IID_IDeckLinkInput, (void**) &m_decklinkInput ) != S_OK )
133                                 throw "No DeckLink cards support input.";
134
135                         // Provide this class as a delegate to the input callback
136                         m_decklinkInput->SetCallback( this );
137
138                         // Initialize other members
139                         pthread_mutex_init( &m_mutex, NULL );
140                         pthread_cond_init( &m_condition, NULL );
141                         m_queue = mlt_deque_init();
142                         m_started = false;
143                         m_dropped = 0;
144                         m_isBuffering = true;
145                 }
146                 catch ( const char *error )
147                 {
148                         if ( decklinkIterator )
149                                 decklinkIterator->Release();
150                         mlt_log_error( getProducer(), "%s\n", error );
151                         return false;
152                 }
153                 return true;
154         }
155
156         bool start( mlt_profile profile = 0 )
157         {
158                 if ( m_started )
159                         return false;
160                 try
161                 {
162                         // Initialize some members
163                         m_vancLines = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "vanc" );
164                         if ( m_vancLines == -1 )
165                                 m_vancLines = profile->height <= 512 ? 26 : 32;
166
167                         if ( !profile )
168                                 profile = mlt_service_profile( MLT_PRODUCER_SERVICE( getProducer() ) );
169
170                         // Get the display mode
171                         BMDDisplayMode displayMode = getDisplayMode( profile, m_vancLines );
172                         if ( displayMode == (BMDDisplayMode) bmdDisplayModeNotSupported )
173                         {
174                                 mlt_log_info( getProducer(), "profile = %dx%d %f fps %s\n", profile->width, profile->height,
175                                                           mlt_profile_fps( profile ), profile->progressive? "progressive" : "interlace" );
176                                 throw "Profile is not compatible with decklink.";
177                         }
178
179                         // Determine if supports input format detection
180 #ifdef WIN32
181                         BOOL doesDetectFormat = FALSE;
182 #else
183                         bool doesDetectFormat = false;
184 #endif
185                         IDeckLinkAttributes *decklinkAttributes = 0;
186                         if ( m_decklink->QueryInterface( IID_IDeckLinkAttributes, (void**) &decklinkAttributes ) == S_OK )
187                         {
188                                 if ( decklinkAttributes->GetFlag( BMDDeckLinkSupportsInputFormatDetection, &doesDetectFormat ) != S_OK )
189                                         doesDetectFormat = false;
190                                 decklinkAttributes->Release();
191                         }
192                         mlt_log_verbose( getProducer(), "%s format detection\n", doesDetectFormat ? "supports" : "does not support" );
193
194                         // Enable video capture
195                         BMDPixelFormat pixelFormat = bmdFormat8BitYUV;
196                         BMDVideoInputFlags flags = doesDetectFormat ? bmdVideoInputEnableFormatDetection : bmdVideoInputFlagDefault;
197                         if ( S_OK != m_decklinkInput->EnableVideoInput( displayMode, pixelFormat, flags ) )
198                                 throw "Failed to enable video capture.";
199
200                         // Enable audio capture
201                         BMDAudioSampleRate sampleRate = bmdAudioSampleRate48kHz;
202                         BMDAudioSampleType sampleType = bmdAudioSampleType16bitInteger;
203                         int channels = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "channels" );
204                         if ( S_OK != m_decklinkInput->EnableAudioInput( sampleRate, sampleType, channels ) )
205                                 throw "Failed to enable audio capture.";
206
207                         // Start capture
208                         m_dropped = 0;
209                         mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "dropped", m_dropped );
210                         m_started = m_decklinkInput->StartStreams() == S_OK;
211                         if ( !m_started )
212                                 throw "Failed to start capture.";
213                 }
214                 catch ( const char *error )
215                 {
216                         m_decklinkInput->DisableVideoInput();
217                         mlt_log_error( getProducer(), "%s\n", error );
218                         return false;
219                 }
220                 return true;
221         }
222
223         void stop()
224         {
225                 if ( !m_started )
226                         return;
227                 m_started = false;
228
229                 // Release the wait in getFrame
230                 pthread_mutex_lock( &m_mutex );
231                 pthread_cond_broadcast( &m_condition );
232                 pthread_mutex_unlock( &m_mutex );
233
234                 m_decklinkInput->StopStreams();
235
236                 // Cleanup queue
237                 pthread_mutex_lock( &m_mutex );
238                 while ( mlt_frame frame = (mlt_frame) mlt_deque_pop_back( m_queue ) )
239                         mlt_frame_close( frame );
240                 pthread_mutex_unlock( &m_mutex );
241         }
242
243         mlt_frame getFrame()
244         {
245                 mlt_frame frame = NULL;
246                 struct timeval now;
247                 struct timespec tm;
248                 double fps = mlt_producer_get_fps( getProducer() );
249
250                 // Allow the buffer to fill to the requested initial buffer level.
251                 if ( m_isBuffering )
252                 {
253                         int prefill = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "prefill" );
254                         int buffer = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "buffer" );
255
256                         m_isBuffering = false;
257                         prefill = prefill > buffer ? buffer : prefill;
258                         pthread_mutex_lock( &m_mutex );
259                         while ( mlt_deque_count( m_queue ) < prefill )
260                         {
261                                 // Wait up to buffer/fps seconds
262                                 gettimeofday( &now, NULL );
263                                 long usec = now.tv_sec * 1000000 + now.tv_usec;
264                                 usec += 1000000 * buffer / fps;
265                                 tm.tv_sec = usec / 1000000;
266                                 tm.tv_nsec = (usec % 1000000) * 1000;
267                                 if ( pthread_cond_timedwait( &m_condition, &m_mutex, &tm ) )
268                                         break;
269                         }
270                         pthread_mutex_unlock( &m_mutex );
271                 }
272
273                 // Wait if queue is empty
274                 pthread_mutex_lock( &m_mutex );
275                 while ( mlt_deque_count( m_queue ) < 1 )
276                 {
277                         // Wait up to twice frame duration
278                         gettimeofday( &now, NULL );
279                         long usec = now.tv_sec * 1000000 + now.tv_usec;
280                         usec += 2000000 / fps;
281                         tm.tv_sec = usec / 1000000;
282                         tm.tv_nsec = (usec % 1000000) * 1000;
283                         if ( pthread_cond_timedwait( &m_condition, &m_mutex, &tm ) )
284                                 // Stop waiting if error (timed out)
285                                 break;
286                 }
287
288                 // Get the first frame from the queue
289                 frame = ( mlt_frame ) mlt_deque_pop_front( m_queue );
290                 pthread_mutex_unlock( &m_mutex );
291
292                 // Set frame timestamp and properties
293                 if ( frame )
294                 {
295                         mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( getProducer() ) );
296                         mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
297                         mlt_properties_set_int( properties, "progressive", profile->progressive );
298                         mlt_properties_set_int( properties, "meta.media.progressive", profile->progressive );
299                         mlt_properties_set_int( properties, "top_field_first", m_topFieldFirst );
300                         mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) );
301                         mlt_properties_set_int( properties, "meta.media.sample_aspect_num", profile->sample_aspect_num );
302                         mlt_properties_set_int( properties, "meta.media.sample_aspect_den", profile->sample_aspect_den );
303                         mlt_properties_set_int( properties, "meta.media.frame_rate_num", profile->frame_rate_num );
304                         mlt_properties_set_int( properties, "meta.media.frame_rate_den", profile->frame_rate_den );
305                         mlt_properties_set_int( properties, "width", profile->width );
306                         mlt_properties_set_int( properties, "real_width", profile->width );
307                         mlt_properties_set_int( properties, "meta.media.width", profile->width );
308                         mlt_properties_set_int( properties, "height", profile->height );
309                         mlt_properties_set_int( properties, "real_height", profile->height );
310                         mlt_properties_set_int( properties, "meta.media.height", profile->height );
311                         mlt_properties_set_int( properties, "format", mlt_image_yuv422 );
312                         mlt_properties_set_int( properties, "colorspace", m_colorspace );
313                         mlt_properties_set_int( properties, "meta.media.colorspace", m_colorspace );
314                         mlt_properties_set_int( properties, "audio_frequency", 48000 );
315                         mlt_properties_set_int( properties, "audio_channels",
316                                 mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "channels" ) );
317                 }
318                 return frame;
319         }
320
321         // *** DeckLink API implementation of IDeckLinkInputCallback *** //
322
323         // IUnknown needs only a dummy implementation
324         virtual HRESULT STDMETHODCALLTYPE QueryInterface( REFIID iid, LPVOID *ppv )
325                 { return E_NOINTERFACE; }
326         virtual ULONG STDMETHODCALLTYPE AddRef()
327                 { return 1; }
328         virtual ULONG STDMETHODCALLTYPE Release()
329                 { return 1; }
330
331         /************************* DeckLink API Delegate Methods *****************************/
332
333         virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(
334                         IDeckLinkVideoInputFrame* video,
335                         IDeckLinkAudioInputPacket* audio )
336         {
337                 // Create mlt_frame
338                 mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( getProducer() ) );
339
340                 // Copy video
341                 if ( video )
342                 {
343                         if ( !( video->GetFlags() & bmdFrameHasNoInputSource ) )
344                         {
345                                 int size = video->GetRowBytes() * ( video->GetHeight() + m_vancLines );
346                                 void* image = mlt_pool_alloc( size );
347                                 void* buffer = 0;
348                                 unsigned char* p = (unsigned char*) image;
349                                 int n = size / 2;
350 \
351                                 // Initialize VANC lines to nominal black
352                                 while ( --n )
353                                 {
354                                         *p ++ = 16;
355                                         *p ++ = 128;
356                                 }
357
358                                 // Capture VANC
359                                 if ( m_vancLines > 0 )
360                                 {
361                                         IDeckLinkVideoFrameAncillary* vanc = 0;
362                                         if ( video->GetAncillaryData( &vanc ) == S_OK && vanc )
363                                         {
364                                                 for ( int i = 1; i < m_vancLines + 1; i++ )
365                                                 {
366                                                         if ( vanc->GetBufferForVerticalBlankingLine( i, &buffer ) == S_OK )
367                                                                 swab( (char*) buffer, (char*) image + ( i - 1 ) * video->GetRowBytes(), video->GetRowBytes() );
368                                                         else
369                                                                 mlt_log_debug( getProducer(), "failed capture vanc line %d\n", i );
370                                                 }
371                                                 vanc->Release();
372                                         }
373                                 }
374
375                                 // Capture image
376                                 video->GetBytes( &buffer );
377                                 if ( image && buffer )
378                                 {
379                                         size =  video->GetRowBytes() * video->GetHeight();
380                                         swab( (char*) buffer, (char*) image + m_vancLines * video->GetRowBytes(), size );
381                                         mlt_frame_set_image( frame, (uint8_t*) image, size, mlt_pool_release );
382                                 }
383                                 else if ( image )
384                                 {
385                                         mlt_log_verbose( getProducer(), "no video\n" );
386                                         mlt_pool_release( image );
387                                 }
388                         }
389                         else
390                         {
391                                 mlt_log_verbose( getProducer(), "no signal\n" );
392                                 mlt_frame_close( frame );
393                                 frame = 0;
394                         }
395
396                         // Get timecode
397                         IDeckLinkTimecode* timecode = 0;
398                         if ( video->GetTimecode( bmdTimecodeVITC, &timecode ) == S_OK && timecode )
399                         {
400                                 const char* timecodeString = 0;
401
402 #ifdef WIN32
403                                 if ( timecode->GetString( (BSTR*) &timecodeString ) == S_OK )
404 #else
405                                 if ( timecode->GetString( &timecodeString ) == S_OK )
406 #endif
407                                 {
408                                         mlt_properties_set( MLT_FRAME_PROPERTIES( frame ), "meta.attr.vitc.markup", timecodeString );
409                                         mlt_log_debug( getProducer(), "timecode %s\n", timecodeString );
410                                 }
411                                 if ( timecodeString )
412                                         free( (void*) timecodeString );
413                                 timecode->Release();
414                         }
415                 }
416                 else
417                 {
418                         mlt_log_verbose( getProducer(), "no video\n" );
419                         mlt_frame_close( frame );
420                         frame = 0;
421                 }
422
423                 // Copy audio
424                 if ( frame && audio )
425                 {
426                         int channels = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "channels" );
427                         int size = audio->GetSampleFrameCount() * channels * sizeof(int16_t);
428                         mlt_audio_format format = mlt_audio_s16;
429                         void* pcm = mlt_pool_alloc( size );
430                         void* buffer = 0;
431
432                         audio->GetBytes( &buffer );
433                         if ( buffer )
434                         {
435                                 memcpy( pcm, buffer, size );
436                                 mlt_frame_set_audio( frame, pcm, format, size, mlt_pool_release );
437                                 mlt_properties_set_int( MLT_FRAME_PROPERTIES(frame), "audio_samples", audio->GetSampleFrameCount() );
438                         }
439                         else
440                         {
441                                 mlt_log_verbose( getProducer(), "no audio\n" );
442                                 mlt_pool_release( pcm );
443                         }
444                 }
445                 else
446                 {
447                         mlt_log_verbose( getProducer(), "no audio\n" );
448                 }
449
450                 // Put frame in queue
451                 if ( frame )
452                 {
453                         int queueMax = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "buffer" );
454                         pthread_mutex_lock( &m_mutex );
455                         if ( mlt_deque_count( m_queue ) < queueMax )
456                         {
457                                 mlt_deque_push_back( m_queue, frame );
458                                 pthread_cond_broadcast( &m_condition );
459                         }
460                         else
461                         {
462                                 mlt_frame_close( frame );
463                                 mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "dropped", ++m_dropped );
464                                 mlt_log_warning( getProducer(), "frame dropped %d\n", m_dropped );
465                         }
466                         pthread_mutex_unlock( &m_mutex );
467                 }
468
469                 return S_OK;
470         }
471
472         virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged(
473                         BMDVideoInputFormatChangedEvents events,
474                         IDeckLinkDisplayMode* mode,
475                         BMDDetectedVideoInputFormatFlags flags )
476         {
477                 mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( getProducer() ) );
478                 if ( events & bmdVideoInputDisplayModeChanged )
479                 {
480                         BMDTimeValue duration;
481                         BMDTimeScale timescale;
482                         mode->GetFrameRate( &duration, &timescale );
483                         profile->width = mode->GetWidth();
484                         profile->height = mode->GetHeight() + m_vancLines;
485                         profile->frame_rate_num = timescale;
486                         profile->frame_rate_den = duration;
487                         if ( profile->width == 720 )
488                         {
489                                 if ( profile->height == 576 )
490                                 {
491                                         profile->sample_aspect_num = 16;
492                                         profile->sample_aspect_den = 15;
493                                 }
494                                 else
495                                 {
496                                         profile->sample_aspect_num = 8;
497                                         profile->sample_aspect_den = 9;
498                                 }
499                                 profile->display_aspect_num = 4;
500                                 profile->display_aspect_den = 3;
501                         }
502                         else
503                         {
504                                 profile->sample_aspect_num = 1;
505                                 profile->sample_aspect_den = 1;
506                                 profile->display_aspect_num = 16;
507                                 profile->display_aspect_den = 9;
508                         }
509                         free( profile->description );
510                         profile->description = strdup( "decklink" );
511                         mlt_log_verbose( getProducer(), "format changed %dx%d %.3f fps\n",
512                                 profile->width, profile->height, (double) profile->frame_rate_num / profile->frame_rate_den );
513                 }
514                 if ( events & bmdVideoInputFieldDominanceChanged )
515                 {
516                         profile->progressive = mode->GetFieldDominance() == bmdProgressiveFrame;
517                         m_topFieldFirst = mode->GetFieldDominance() == bmdUpperFieldFirst;
518                         mlt_log_verbose( getProducer(), "field dominance changed prog %d tff %d\n",
519                                 profile->progressive, m_topFieldFirst );
520                 }
521                 if ( events & bmdVideoInputColorspaceChanged )
522                 {
523                         profile->colorspace = m_colorspace =
524                                 ( mode->GetFlags() & bmdDisplayModeColorspaceRec709 ) ? 709 : 601;
525                         mlt_log_verbose( getProducer(), "colorspace changed %d\n", profile->colorspace );
526                 }
527                 return S_OK;
528         }
529 };
530
531 static int get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
532 {
533         return mlt_frame_get_audio( frame, (void**) buffer, format, frequency, channels, samples );
534 }
535
536 static int get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
537 {
538         return mlt_frame_get_image( frame, buffer, format, width, height, writable );
539 }
540
541 static int get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
542 {
543         DeckLinkProducer* decklink = (DeckLinkProducer*) producer->child;
544         mlt_position pos = mlt_producer_position( producer );
545         mlt_position end = mlt_producer_get_playtime( producer );
546         end = ( mlt_producer_get_length( producer ) < end ? mlt_producer_get_length( producer ) : end ) - 1;
547
548         // Re-open if needed
549         if ( !decklink && pos < end )
550         {
551                 producer->child = decklink = new DeckLinkProducer();
552                 decklink->setProducer( producer );
553                 decklink->open( mlt_properties_get_int( MLT_PRODUCER_PROPERTIES(producer), "resource" ) );
554         }
555
556         // Start if needed
557         if ( decklink )
558         {
559                 decklink->start( mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ) );
560
561                 // Get the next frame from the decklink object
562                 if ( ( *frame = decklink->getFrame() ))
563                 {
564                         // Add audio and video getters
565                         mlt_frame_push_audio( *frame, (void*) get_audio );
566                         mlt_frame_push_get_image( *frame, get_image );
567                 }
568         }
569         if ( !*frame )
570                 *frame = mlt_frame_init( MLT_PRODUCER_SERVICE(producer) );
571
572         // Calculate the next timecode
573         mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
574         mlt_producer_prepare_next( producer );
575
576         // Close DeckLink if at end
577         if ( pos >= end && decklink )
578         {
579                 decklink->stop();
580                 delete decklink;
581                 producer->child = NULL;
582         }
583
584         return 0;
585 }
586
587 static void producer_close( mlt_producer producer )
588 {
589         delete (DeckLinkProducer*) producer->child;
590         producer->close = NULL;
591         mlt_producer_close( producer );
592 }
593
594 extern "C" {
595
596 /** Initialise the producer.
597  */
598
599 mlt_producer producer_decklink_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
600 {
601         // Allocate the producer
602         DeckLinkProducer* decklink = new DeckLinkProducer();
603         mlt_producer producer = (mlt_producer) calloc( 1, sizeof( *producer ) );
604
605         // If allocated and initializes
606         if ( decklink && !mlt_producer_init( producer, decklink ) )
607         {
608                 if ( decklink->open( arg? atoi( arg ) : 0 ) )
609                 {
610                         mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
611
612                         // Close DeckLink and defer re-open to get_frame
613                         delete decklink;
614                         producer->child = NULL;
615
616                         // Set callbacks
617                         producer->close = (mlt_destructor) producer_close;
618                         producer->get_frame = get_frame;
619
620                         // Set properties
621                         mlt_properties_set( properties, "resource", (arg && strcmp( arg, ""))? arg : "0" );
622                         mlt_properties_set_int( properties, "channels", 2 );
623                         mlt_properties_set_int( properties, "buffer", 25 );
624                         mlt_properties_set_int( properties, "prefill", 25 );
625
626                         // These properties effectively make it infinite.
627                         mlt_properties_set_int( properties, "length", INT_MAX );
628                         mlt_properties_set_int( properties, "out", INT_MAX - 1 );
629                         mlt_properties_set( properties, "eof", "loop" );
630                 }
631         }
632
633         return producer;
634 }
635
636 }