]> git.sesse.net Git - mlt/blobdiff - src/modules/decklink/producer_decklink.cpp
Fix compiler warnings due to non-virtual destructors.
[mlt] / src / modules / decklink / producer_decklink.cpp
index 8f0411276e5e32c3b4902315bc12a0284a13ec17..877c0eacc400742e3ea686e92eb3b2613b74f94e 100644 (file)
 #include <unistd.h>
 #include <limits.h>
 #include <sys/time.h>
-#include "DeckLinkAPI.h"
+#include "common.h"
 
 class DeckLinkProducer
        : public IDeckLinkInputCallback
 {
 private:
-       mlt_producer_s   m_producer;
+       mlt_producer     m_producer;
        IDeckLink*       m_decklink;
        IDeckLinkInput*  m_decklinkInput;
        mlt_deque        m_queue;
@@ -39,12 +39,17 @@ private:
        bool             m_started;
        int              m_dropped;
        bool             m_isBuffering;
+       int              m_topFieldFirst;
+       int              m_colorspace;
+       int              m_vancLines;
+       mlt_cache        m_cache;
+       bool             m_reprio;
 
-       BMDDisplayMode getDisplayMode( mlt_profile profile )
+       BMDDisplayMode getDisplayMode( mlt_profile profile, int vancLines )
        {
-               IDeckLinkDisplayModeIterator* iter;
-               IDeckLinkDisplayMode* mode;
-               BMDDisplayMode result = bmdDisplayModeNotSupported;
+               IDeckLinkDisplayModeIterator* iter = NULL;
+               IDeckLinkDisplayMode* mode = NULL;
+               BMDDisplayMode result = (BMDDisplayMode) bmdDisplayModeNotSupported;
 
                if ( m_decklinkInput->GetDisplayModeIterator( &iter ) == S_OK )
                {
@@ -57,12 +62,17 @@ private:
                                mode->GetFrameRate( &duration, &timescale );
                                double fps = (double) timescale / duration;
                                int p = mode->GetFieldDominance() == bmdProgressiveFrame;
-                               mlt_log_verbose( getProducer(), "BMD mode %dx%d %.3f fps prog %d\n", width, height, fps, p );
+                               m_topFieldFirst = mode->GetFieldDominance() == bmdUpperFieldFirst;
+                               m_colorspace = ( mode->GetFlags() & bmdDisplayModeColorspaceRec709 ) ? 709 : 601;
+                               mlt_log_verbose( getProducer(), "BMD mode %dx%d %.3f fps prog %d tff %d\n", width, height, fps, p, m_topFieldFirst );
 
-                               if ( width == profile->width && height == profile->height && p == profile->progressive
-                                        && fps == mlt_profile_fps( profile ) )
+                               if ( width == profile->width && p == profile->progressive
+                                        && ( height + vancLines == profile->height || ( height == 486 && profile->height == 480 + vancLines ) )
+                                        && (int) fps == (int) mlt_profile_fps( profile ) )
                                        result = mode->GetDisplayMode();
+                               SAFE_RELEASE( mode );
                        }
+                       SAFE_RELEASE( iter );
                }
 
                return result;
@@ -70,39 +80,61 @@ private:
 
 public:
 
-       mlt_producer getProducer()
-               { return &m_producer; }
+       void setProducer( mlt_producer producer )
+               { m_producer = producer; }
 
-       ~DeckLinkProducer()
+       mlt_producer getProducer() const
+               { return m_producer; }
+
+       DeckLinkProducer()
+       {
+               m_producer = NULL;
+               m_decklink = NULL;
+               m_decklinkInput = NULL;
+       }
+
+       virtual ~DeckLinkProducer()
        {
-               if ( m_decklinkInput )
-                       m_decklinkInput->Release();
-               if ( m_decklink )
-                       m_decklink->Release();
                if ( m_queue )
                {
                        stop();
                        mlt_deque_close( m_queue );
                        pthread_mutex_destroy( &m_mutex );
                        pthread_cond_destroy( &m_condition );
+                       mlt_cache_close( m_cache );
                }
+               SAFE_RELEASE( m_decklinkInput );
+               SAFE_RELEASE( m_decklink );
        }
 
-       bool open( mlt_profile profile, unsigned card =  0 )
+       bool open( unsigned card =  0 )
        {
-               IDeckLinkIterator* decklinkIterator = CreateDeckLinkIteratorInstance();
+               IDeckLinkIterator* decklinkIterator = NULL;
                try
                {
+#ifdef WIN32
+                       HRESULT result =  CoInitialize( NULL );
+                       if ( FAILED( result ) )
+                               throw "COM initialization failed";
+                       result = CoCreateInstance( CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**) &decklinkIterator );
+                       if ( FAILED( result ) )
+                               throw "The DeckLink drivers are not installed.";
+#else
+                       decklinkIterator = CreateDeckLinkIteratorInstance();
                        if ( !decklinkIterator )
                                throw "The DeckLink drivers are not installed.";
-
+#endif
                        // Connect to the Nth DeckLink instance
-                       unsigned i = 0;
-                       do {
-                               if ( decklinkIterator->Next( &m_decklink ) != S_OK )
-                                       throw "DeckLink card not found.";
-                       } while ( ++i <= card );
-                       decklinkIterator->Release();
+                       for ( unsigned i = 0; decklinkIterator->Next( &m_decklink ) == S_OK ; i++)
+                       {
+                               if ( i == card )
+                                       break;
+                               else
+                                       SAFE_RELEASE( m_decklink );
+                       }
+                       SAFE_RELEASE( decklinkIterator );
+                       if ( !m_decklink )
+                               throw "DeckLink card not found.";
 
                        // Get the input interface
                        if ( m_decklink->QueryInterface( IID_IDeckLinkInput, (void**) &m_decklinkInput ) != S_OK )
@@ -118,11 +150,15 @@ public:
                        m_started = false;
                        m_dropped = 0;
                        m_isBuffering = true;
+                       m_cache = mlt_cache_init();
+
+                       // 3 covers YADIF and increasing framerate use cases
+                       mlt_cache_set_size( m_cache, 3 );
                }
                catch ( const char *error )
                {
-                       if ( decklinkIterator )
-                               decklinkIterator->Release();
+                       SAFE_RELEASE( m_decklinkInput );
+                       SAFE_RELEASE( m_decklink );
                        mlt_log_error( getProducer(), "%s\n", error );
                        return false;
                }
@@ -135,17 +171,41 @@ public:
                        return false;
                try
                {
+                       // Initialize some members
+                       m_vancLines = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "vanc" );
+                       if ( m_vancLines == -1 )
+                               m_vancLines = profile->height <= 512 ? 26 : 32;
+
                        if ( !profile )
                                profile = mlt_service_profile( MLT_PRODUCER_SERVICE( getProducer() ) );
 
                        // Get the display mode
-                       BMDDisplayMode displayMode = getDisplayMode( profile );
-                       if ( displayMode == bmdDisplayModeNotSupported )
+                       BMDDisplayMode displayMode = getDisplayMode( profile, m_vancLines );
+                       if ( displayMode == (BMDDisplayMode) bmdDisplayModeNotSupported )
+                       {
+                               mlt_log_info( getProducer(), "profile = %dx%d %f fps %s\n", profile->width, profile->height,
+                                                         mlt_profile_fps( profile ), profile->progressive? "progressive" : "interlace" );
                                throw "Profile is not compatible with decklink.";
+                       }
+
+                       // Determine if supports input format detection
+#ifdef WIN32
+                       BOOL doesDetectFormat = FALSE;
+#else
+                       bool doesDetectFormat = false;
+#endif
+                       IDeckLinkAttributes *decklinkAttributes = 0;
+                       if ( m_decklink->QueryInterface( IID_IDeckLinkAttributes, (void**) &decklinkAttributes ) == S_OK )
+                       {
+                               if ( decklinkAttributes->GetFlag( BMDDeckLinkSupportsInputFormatDetection, &doesDetectFormat ) != S_OK )
+                                       doesDetectFormat = false;
+                               SAFE_RELEASE( decklinkAttributes );
+                       }
+                       mlt_log_verbose( getProducer(), "%s format detection\n", doesDetectFormat ? "supports" : "does not support" );
 
                        // Enable video capture
                        BMDPixelFormat pixelFormat = bmdFormat8BitYUV;
-                       BMDVideoInputFlags flags = bmdVideoInputFlagDefault;
+                       BMDVideoInputFlags flags = doesDetectFormat ? bmdVideoInputEnableFormatDetection : bmdVideoInputFlagDefault;
                        if ( S_OK != m_decklinkInput->EnableVideoInput( displayMode, pixelFormat, flags ) )
                                throw "Failed to enable video capture.";
 
@@ -174,8 +234,9 @@ public:
 
        void stop()
        {
-               if ( m_started )
+               if ( !m_started )
                        return;
+               m_started = false;
 
                // Release the wait in getFrame
                pthread_mutex_lock( &m_mutex );
@@ -183,22 +244,23 @@ public:
                pthread_mutex_unlock( &m_mutex );
 
                m_decklinkInput->StopStreams();
+               m_decklinkInput->DisableVideoInput();
+               m_decklinkInput->DisableAudioInput();
 
                // Cleanup queue
                pthread_mutex_lock( &m_mutex );
                while ( mlt_frame frame = (mlt_frame) mlt_deque_pop_back( m_queue ) )
                        mlt_frame_close( frame );
                pthread_mutex_unlock( &m_mutex );
-
-               m_started = false;
        }
 
        mlt_frame getFrame()
        {
-               mlt_frame frame = NULL;
                struct timeval now;
                struct timespec tm;
                double fps = mlt_producer_get_fps( getProducer() );
+               mlt_position position = mlt_producer_position( getProducer() );
+               mlt_frame frame = mlt_cache_get_frame( m_cache, position );
 
                // Allow the buffer to fill to the requested initial buffer level.
                if ( m_isBuffering )
@@ -223,23 +285,32 @@ public:
                        pthread_mutex_unlock( &m_mutex );
                }
 
-               // Wait if queue is empty
-               pthread_mutex_lock( &m_mutex );
-               while ( mlt_deque_count( m_queue ) < 1 )
+               if ( !frame )
                {
-                       // Wait up to twice frame duration
-                       gettimeofday( &now, NULL );
-                       tm.tv_sec = now.tv_sec;
-                       now.tv_usec += 2000000 / fps;
-                       tm.tv_nsec = now.tv_usec * 1000;
-                       if ( pthread_cond_timedwait( &m_condition, &m_mutex, &tm ) )
-                               // Stop waiting if error (timed out)
-                               break;
-               }
+                       // Wait if queue is empty
+                       pthread_mutex_lock( &m_mutex );
+                       while ( mlt_deque_count( m_queue ) < 1 )
+                       {
+                               // Wait up to twice frame duration
+                               gettimeofday( &now, NULL );
+                               long usec = now.tv_sec * 1000000 + now.tv_usec;
+                               usec += 2000000 / fps;
+                               tm.tv_sec = usec / 1000000;
+                               tm.tv_nsec = (usec % 1000000) * 1000;
+                               if ( pthread_cond_timedwait( &m_condition, &m_mutex, &tm ) )
+                                       // Stop waiting if error (timed out)
+                                       break;
+                       }
+                       frame = ( mlt_frame ) mlt_deque_pop_front( m_queue );
+                       pthread_mutex_unlock( &m_mutex );
 
-               // Get the first frame from the queue
-               frame = ( mlt_frame ) mlt_deque_pop_front( m_queue );
-               pthread_mutex_unlock( &m_mutex );
+                       // add to cache
+                       if ( frame )
+                       {
+                               mlt_frame_set_position( frame, position );
+                               mlt_cache_put_frame( m_cache, frame );
+                       }
+               }
 
                // Set frame timestamp and properties
                if ( frame )
@@ -247,16 +318,27 @@ public:
                        mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( getProducer() ) );
                        mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
                        mlt_properties_set_int( properties, "progressive", profile->progressive );
+                       mlt_properties_set_int( properties, "meta.media.progressive", profile->progressive );
+                       mlt_properties_set_int( properties, "top_field_first", m_topFieldFirst );
                        mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) );
+                       mlt_properties_set_int( properties, "meta.media.sample_aspect_num", profile->sample_aspect_num );
+                       mlt_properties_set_int( properties, "meta.media.sample_aspect_den", profile->sample_aspect_den );
+                       mlt_properties_set_int( properties, "meta.media.frame_rate_num", profile->frame_rate_num );
+                       mlt_properties_set_int( properties, "meta.media.frame_rate_den", profile->frame_rate_den );
                        mlt_properties_set_int( properties, "width", profile->width );
-                       mlt_properties_set_int( properties, "real_width", profile->width );
+                       mlt_properties_set_int( properties, "meta.media.width", profile->width );
                        mlt_properties_set_int( properties, "height", profile->height );
-                       mlt_properties_set_int( properties, "real_height", profile->height );
+                       mlt_properties_set_int( properties, "meta.media.height", profile->height );
                        mlt_properties_set_int( properties, "format", mlt_image_yuv422 );
+                       mlt_properties_set_int( properties, "colorspace", m_colorspace );
+                       mlt_properties_set_int( properties, "meta.media.colorspace", m_colorspace );
                        mlt_properties_set_int( properties, "audio_frequency", 48000 );
                        mlt_properties_set_int( properties, "audio_channels",
                                mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "channels" ) );
                }
+               else
+                       mlt_log_warning( getProducer(), "buffer underrun\n" );
+
                return frame;
        }
 
@@ -276,6 +358,50 @@ public:
                        IDeckLinkVideoInputFrame* video,
                        IDeckLinkAudioInputPacket* audio )
        {
+               if( !m_reprio )
+               {
+                       mlt_properties properties = MLT_PRODUCER_PROPERTIES( getProducer() );
+
+                       if ( mlt_properties_get( properties, "priority" ) )
+                       {
+                               int r;
+                               pthread_t thread;
+                               pthread_attr_t tattr;
+                               struct sched_param param;
+
+                               pthread_attr_init(&tattr);
+                               pthread_attr_setschedpolicy(&tattr, SCHED_FIFO);
+
+                               if ( !strcmp( "max", mlt_properties_get( properties, "priority" ) ) )
+                                       param.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1;
+                               else if ( !strcmp( "min", mlt_properties_get( properties, "priority" ) ) )
+                                       param.sched_priority = sched_get_priority_min(SCHED_FIFO) + 1;
+                               else
+                                       param.sched_priority = mlt_properties_get_int( properties, "priority" );
+
+                               pthread_attr_setschedparam(&tattr, &param);
+
+                               thread = pthread_self();
+
+                               r = pthread_setschedparam(thread, SCHED_FIFO, &param);
+                               if( r )
+                                       mlt_log_verbose( getProducer(),
+                                               "VideoInputFrameArrived: pthread_setschedparam returned %d\n", r);
+                               else
+                                       mlt_log_verbose( getProducer(),
+                                               "VideoInputFrameArrived: param.sched_priority=%d\n", param.sched_priority);
+                       };
+
+                       m_reprio = true;
+               };
+
+               if ( mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "preview" ) &&
+                       mlt_producer_get_speed( getProducer() ) == 0.0 && !mlt_deque_count( m_queue ))
+               {
+                       pthread_cond_broadcast( &m_condition );
+                       return S_OK;
+               }
+
                // Create mlt_frame
                mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( getProducer() ) );
 
@@ -284,14 +410,42 @@ public:
                {
                        if ( !( video->GetFlags() & bmdFrameHasNoInputSource ) )
                        {
-                               int size = video->GetRowBytes() * video->GetHeight();
+                               int size = video->GetRowBytes() * ( video->GetHeight() + m_vancLines );
                                void* image = mlt_pool_alloc( size );
                                void* buffer = 0;
+                               unsigned char* p = (unsigned char*) image;
+                               int n = size / 2;
+\
+                               // Initialize VANC lines to nominal black
+                               while ( --n )
+                               {
+                                       *p ++ = 16;
+                                       *p ++ = 128;
+                               }
+
+                               // Capture VANC
+                               if ( m_vancLines > 0 )
+                               {
+                                       IDeckLinkVideoFrameAncillary* vanc = 0;
+                                       if ( video->GetAncillaryData( &vanc ) == S_OK && vanc )
+                                       {
+                                               for ( int i = 1; i < m_vancLines + 1; i++ )
+                                               {
+                                                       if ( vanc->GetBufferForVerticalBlankingLine( i, &buffer ) == S_OK )
+                                                               swab2( (char*) buffer, (char*) image + ( i - 1 ) * video->GetRowBytes(), video->GetRowBytes() );
+                                                       else
+                                                               mlt_log_debug( getProducer(), "failed capture vanc line %d\n", i );
+                                               }
+                                               SAFE_RELEASE(vanc);
+                                       }
+                               }
 
+                               // Capture image
                                video->GetBytes( &buffer );
                                if ( image && buffer )
                                {
-                                       swab( buffer, image, size );
+                                       size =  video->GetRowBytes() * video->GetHeight();
+                                       swab2( (char*) buffer, (char*) image + m_vancLines * video->GetRowBytes(), size );
                                        mlt_frame_set_image( frame, (uint8_t*) image, size, mlt_pool_release );
                                }
                                else if ( image )
@@ -306,6 +460,23 @@ public:
                                mlt_frame_close( frame );
                                frame = 0;
                        }
+
+                       // Get timecode
+                       IDeckLinkTimecode* timecode = 0;
+                       if ( video->GetTimecode( bmdTimecodeVITC, &timecode ) == S_OK && timecode )
+                       {
+                               DLString timecodeString = 0;
+
+                               if ( timecode->GetString( &timecodeString ) == S_OK )
+                               {
+                                       char* s = getCString( timecodeString );
+                                       mlt_properties_set( MLT_FRAME_PROPERTIES( frame ), "meta.attr.vitc.markup", s );
+                                       mlt_log_debug( getProducer(), "timecode %s\n", s );
+                                       freeCString( s );
+                               }
+                               freeDLString( timecodeString );
+                               SAFE_RELEASE( timecode );
+                       }
                }
                else
                {
@@ -355,7 +526,7 @@ public:
                        {
                                mlt_frame_close( frame );
                                mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "dropped", ++m_dropped );
-                               mlt_log_warning( getProducer(), "frame dropped %d\n", m_dropped );
+                               mlt_log_warning( getProducer(), "buffer overrun, frame dropped %d\n", m_dropped );
                        }
                        pthread_mutex_unlock( &m_mutex );
                }
@@ -368,6 +539,56 @@ public:
                        IDeckLinkDisplayMode* mode,
                        BMDDetectedVideoInputFormatFlags flags )
        {
+               mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( getProducer() ) );
+               if ( events & bmdVideoInputDisplayModeChanged )
+               {
+                       BMDTimeValue duration;
+                       BMDTimeScale timescale;
+                       mode->GetFrameRate( &duration, &timescale );
+                       profile->width = mode->GetWidth();
+                       profile->height = mode->GetHeight() + m_vancLines;
+                       profile->frame_rate_num = timescale;
+                       profile->frame_rate_den = duration;
+                       if ( profile->width == 720 )
+                       {
+                               if ( profile->height == 576 )
+                               {
+                                       profile->sample_aspect_num = 16;
+                                       profile->sample_aspect_den = 15;
+                               }
+                               else
+                               {
+                                       profile->sample_aspect_num = 8;
+                                       profile->sample_aspect_den = 9;
+                               }
+                               profile->display_aspect_num = 4;
+                               profile->display_aspect_den = 3;
+                       }
+                       else
+                       {
+                               profile->sample_aspect_num = 1;
+                               profile->sample_aspect_den = 1;
+                               profile->display_aspect_num = 16;
+                               profile->display_aspect_den = 9;
+                       }
+                       free( profile->description );
+                       profile->description = strdup( "decklink" );
+                       mlt_log_verbose( getProducer(), "format changed %dx%d %.3f fps\n",
+                               profile->width, profile->height, (double) profile->frame_rate_num / profile->frame_rate_den );
+               }
+               if ( events & bmdVideoInputFieldDominanceChanged )
+               {
+                       profile->progressive = mode->GetFieldDominance() == bmdProgressiveFrame;
+                       m_topFieldFirst = mode->GetFieldDominance() == bmdUpperFieldFirst;
+                       mlt_log_verbose( getProducer(), "field dominance changed prog %d tff %d\n",
+                               profile->progressive, m_topFieldFirst );
+               }
+               if ( events & bmdVideoInputColorspaceChanged )
+               {
+                       profile->colorspace = m_colorspace =
+                               ( mode->GetFlags() & bmdDisplayModeColorspaceRec709 ) ? 709 : 601;
+                       mlt_log_verbose( getProducer(), "colorspace changed %d\n", profile->colorspace );
+               }
                return S_OK;
        }
 };
@@ -385,32 +606,104 @@ static int get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *forma
 static int get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
 {
        DeckLinkProducer* decklink = (DeckLinkProducer*) producer->child;
+       mlt_position pos = mlt_producer_position( producer );
+       mlt_position end = mlt_producer_get_playtime( producer );
+       end = ( mlt_producer_get_length( producer ) < end ? mlt_producer_get_length( producer ) : end ) - 1;
 
-       // Get the next frame from the decklink object
-       *frame = decklink->getFrame();
+       // Re-open if needed
+       if ( !decklink && pos < end )
+       {
+               producer->child = decklink = new DeckLinkProducer();
+               decklink->setProducer( producer );
+               decklink->open( mlt_properties_get_int( MLT_PRODUCER_PROPERTIES(producer), "resource" ) );
+       }
+
+       // Start if needed
+       if ( decklink )
+       {
+               decklink->start( mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ) );
+
+               // Get the next frame from the decklink object
+               if ( ( *frame = decklink->getFrame() ))
+               {
+                       // Add audio and video getters
+                       mlt_frame_push_audio( *frame, (void*) get_audio );
+                       mlt_frame_push_get_image( *frame, get_image );
+               }
+       }
        if ( !*frame )
-               *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
+               *frame = mlt_frame_init( MLT_PRODUCER_SERVICE(producer) );
 
        // Calculate the next timecode
-       mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
        mlt_producer_prepare_next( producer );
 
-       // Add audio and video getters
-       mlt_frame_push_audio( *frame, (void*) get_audio );
-       mlt_frame_push_get_image( *frame, get_image );
+       // Close DeckLink if at end
+       if ( pos >= end && decklink )
+       {
+               decklink->stop();
+               delete decklink;
+               producer->child = NULL;
+       }
 
        return 0;
 }
 
 static void producer_close( mlt_producer producer )
 {
+       delete (DeckLinkProducer*) producer->child;
        producer->close = NULL;
        mlt_producer_close( producer );
-       delete (DeckLinkProducer*) producer->child;
 }
 
 extern "C" {
 
+// Listen for the list_devices property to be set
+static void on_property_changed( void*, mlt_properties properties, const char *name )
+{
+       IDeckLinkIterator* decklinkIterator = NULL;
+       IDeckLink* decklink = NULL;
+       IDeckLinkInput* decklinkInput = NULL;
+       int i = 0;
+
+       if ( name && !strcmp( name, "list_devices" ) )
+               mlt_event_block( (mlt_event) mlt_properties_get_data( properties, "list-devices-event", NULL ) );
+       else
+               return;
+
+#ifdef WIN32
+       if ( FAILED( CoInitialize( NULL ) ) )
+               return;
+       if ( FAILED( CoCreateInstance( CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**) &decklinkIterator ) ) )
+               return;
+#else
+       if ( !( decklinkIterator = CreateDeckLinkIteratorInstance() ) )
+               return;
+#endif
+       for ( ; decklinkIterator->Next( &decklink ) == S_OK; i++ )
+       {
+               if ( decklink->QueryInterface( IID_IDeckLinkInput, (void**) &decklinkInput ) == S_OK )
+               {
+                       DLString name = NULL;
+                       if ( decklink->GetModelName( &name ) == S_OK )
+                       {
+                               char *name_cstr = getCString( name );
+                               const char *format = "device.%d";
+                               char *key = (char*) calloc( 1, strlen( format ) + 1 );
+
+                               sprintf( key, format, i );
+                               mlt_properties_set( properties, key, name_cstr );
+                               free( key );
+                               freeDLString( name );
+                               freeCString( name_cstr );
+                       }
+                       SAFE_RELEASE( decklinkInput );
+               }
+               SAFE_RELEASE( decklink );
+       }
+       SAFE_RELEASE( decklinkIterator );
+       mlt_properties_set_int( properties, "devices", i );
+}
+
 /** Initialise the producer.
  */
 
@@ -418,22 +711,25 @@ mlt_producer producer_decklink_init( mlt_profile profile, mlt_service_type type,
 {
        // Allocate the producer
        DeckLinkProducer* decklink = new DeckLinkProducer();
-       mlt_producer producer = NULL;
+       mlt_producer producer = (mlt_producer) calloc( 1, sizeof( *producer ) );
 
        // If allocated and initializes
-       if ( decklink && !mlt_producer_init( decklink->getProducer(), decklink ) )
+       if ( decklink && !mlt_producer_init( producer, decklink ) )
        {
-               if ( decklink->open( profile, arg? atoi( arg ) : 0 ) )
+               if ( decklink->open( arg? atoi( arg ) : 0 ) )
                {
-                       producer = decklink->getProducer();
                        mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
 
+                       // Close DeckLink and defer re-open to get_frame
+                       delete decklink;
+                       producer->child = NULL;
+
                        // Set callbacks
                        producer->close = (mlt_destructor) producer_close;
                        producer->get_frame = get_frame;
 
                        // Set properties
-                       mlt_properties_set( properties, "resource", arg? arg : "0" );
+                       mlt_properties_set( properties, "resource", (arg && strcmp( arg, ""))? arg : "0" );
                        mlt_properties_set_int( properties, "channels", 2 );
                        mlt_properties_set_int( properties, "buffer", 25 );
                        mlt_properties_set_int( properties, "prefill", 25 );
@@ -443,12 +739,8 @@ mlt_producer producer_decklink_init( mlt_profile profile, mlt_service_type type,
                        mlt_properties_set_int( properties, "out", INT_MAX - 1 );
                        mlt_properties_set( properties, "eof", "loop" );
 
-                       // Start immediately
-                       if ( !decklink->start( profile ) )
-                       {
-                               producer_close( producer );
-                               producer = NULL;
-                       }
+                       mlt_event event = mlt_events_listen( properties, properties, "property-changed", (mlt_listener) on_property_changed );
+                       mlt_properties_set_data( properties, "list-devices-event", event, 0, NULL, NULL );
                }
        }