]> git.sesse.net Git - mlt/blobdiff - src/modules/decklink/producer_decklink.cpp
fix decklink build for OS X
[mlt] / src / modules / decklink / producer_decklink.cpp
index 8c763a1a4089e9e01ccdbd997c90029ddba26cbc..3ae216c290a6b8b3fac0c087d063ac66c887ad67 100644 (file)
 #include <unistd.h>
 #include <limits.h>
 #include <sys/time.h>
-#ifdef WIN32
-#include <objbase.h>
-#include "DeckLinkAPI_h.h"
-#else
-#include "DeckLinkAPI.h"
-#endif
+#include "common.h"
 
 class DeckLinkProducer
        : public IDeckLinkInputCallback
@@ -47,11 +42,12 @@ private:
        int              m_topFieldFirst;
        int              m_colorspace;
        int              m_vancLines;
+       mlt_cache        m_cache;
 
        BMDDisplayMode getDisplayMode( mlt_profile profile, int vancLines )
        {
-               IDeckLinkDisplayModeIterator* iter;
-               IDeckLinkDisplayMode* mode;
+               IDeckLinkDisplayModeIterator* iter = NULL;
+               IDeckLinkDisplayMode* mode = NULL;
                BMDDisplayMode result = (BMDDisplayMode) bmdDisplayModeNotSupported;
 
                if ( m_decklinkInput->GetDisplayModeIterator( &iter ) == S_OK )
@@ -73,7 +69,9 @@ private:
                                         && ( height + vancLines == profile->height || ( height == 486 && profile->height == 480 + vancLines ) )
                                         && fps == mlt_profile_fps( profile ) )
                                        result = mode->GetDisplayMode();
+                               SAFE_RELEASE( mode );
                        }
+                       SAFE_RELEASE( iter );
                }
 
                return result;
@@ -87,19 +85,24 @@ public:
        mlt_producer getProducer() const
                { return m_producer; }
 
+       DeckLinkProducer()
+       {
+               m_decklink = NULL;
+               m_decklinkInput = NULL;
+       }
+
        ~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( unsigned card =  0 )
@@ -119,14 +122,17 @@ public:
                        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 )
@@ -142,11 +148,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;
                }
@@ -170,7 +180,11 @@ public:
                        // Get the display mode
                        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
@@ -183,7 +197,7 @@ public:
                        {
                                if ( decklinkAttributes->GetFlag( BMDDeckLinkSupportsInputFormatDetection, &doesDetectFormat ) != S_OK )
                                        doesDetectFormat = false;
-                               decklinkAttributes->Release();
+                               SAFE_RELEASE( decklinkAttributes );
                        }
                        mlt_log_verbose( getProducer(), "%s format detection\n", doesDetectFormat ? "supports" : "does not support" );
 
@@ -228,6 +242,8 @@ public:
                pthread_mutex_unlock( &m_mutex );
 
                m_decklinkInput->StopStreams();
+               m_decklinkInput->DisableVideoInput();
+               m_decklinkInput->DisableAudioInput();
 
                // Cleanup queue
                pthread_mutex_lock( &m_mutex );
@@ -242,6 +258,8 @@ public:
                struct timeval now;
                struct timespec tm;
                double fps = mlt_producer_get_fps( getProducer() );
+               mlt_position position = mlt_producer_position( getProducer() );
+               mlt_cache_item cached = mlt_cache_get( m_cache, (void*) position );
 
                // Allow the buffer to fill to the requested initial buffer level.
                if ( m_isBuffering )
@@ -266,24 +284,36 @@ public:
                        pthread_mutex_unlock( &m_mutex );
                }
 
-               // Wait if queue is empty
-               pthread_mutex_lock( &m_mutex );
-               while ( mlt_deque_count( m_queue ) < 1 )
+               if ( cached )
                {
-                       // 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;
+                       // Copy cached frame instead of pulling from queue
+                       frame = mlt_frame_clone( (mlt_frame) mlt_cache_item_data( cached, NULL ), 0 );
+                       mlt_cache_item_close( cached );
                }
+               else
+               {
+                       // 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_cache_put( m_cache, (void*) position, mlt_frame_clone( frame, 1 ), 0,
+                                       (mlt_destructor) mlt_frame_close );
+               }
 
                // Set frame timestamp and properties
                if ( frame )
@@ -299,10 +329,8 @@ public:
                        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 );
@@ -311,6 +339,9 @@ public:
                        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;
        }
 
@@ -330,6 +361,13 @@ public:
                        IDeckLinkVideoInputFrame* video,
                        IDeckLinkAudioInputPacket* audio )
        {
+               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() ) );
 
@@ -364,7 +402,7 @@ public:
                                                        else
                                                                mlt_log_debug( getProducer(), "failed capture vanc line %d\n", i );
                                                }
-                                               vanc->Release();
+                                               SAFE_RELEASE(vanc);
                                        }
                                }
 
@@ -393,20 +431,17 @@ public:
                        IDeckLinkTimecode* timecode = 0;
                        if ( video->GetTimecode( bmdTimecodeVITC, &timecode ) == S_OK && timecode )
                        {
-                               const char* timecodeString = 0;
+                               DLString timecodeString = 0;
 
-#ifdef WIN32
-                               if ( timecode->GetString( (BSTR*) &timecodeString ) == S_OK )
-#else
                                if ( timecode->GetString( &timecodeString ) == S_OK )
-#endif
                                {
-                                       mlt_properties_set( MLT_FRAME_PROPERTIES( frame ), "meta.attr.vitc.markup", timecodeString );
-                                       mlt_log_debug( getProducer(), "timecode %s\n", timecodeString );
+                                       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 );
                                }
-                               if ( timecodeString )
-                                       free( (void*) timecodeString );
-                               timecode->Release();
+                               freeDLString( timecodeString );
+                               SAFE_RELEASE( timecode );
                        }
                }
                else
@@ -589,6 +624,53 @@ static void producer_close( mlt_producer producer )
 
 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.
  */
 
@@ -623,6 +705,9 @@ mlt_producer producer_decklink_init( mlt_profile profile, mlt_service_type type,
                        mlt_properties_set_int( properties, "length", INT_MAX );
                        mlt_properties_set_int( properties, "out", INT_MAX - 1 );
                        mlt_properties_set( properties, "eof", "loop" );
+
+                       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 );
                }
        }