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