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