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