X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;ds=sidebyside;f=src%2Fmodules%2Fdecklink%2Fconsumer_decklink.cpp;h=8260321bc7fd37ea0dcbe6cd0a1b1718e2dd5def;hb=3e2c57731b396081ee8c012c73994e65d8a693e7;hp=bbb0e4e06472c52f3028a8c92352f708c5a43f0b;hpb=c83a24359f54055cf5c2c08bfe77b26bc6418d8c;p=mlt diff --git a/src/modules/decklink/consumer_decklink.cpp b/src/modules/decklink/consumer_decklink.cpp index bbb0e4e0..8260321b 100644 --- a/src/modules/decklink/consumer_decklink.cpp +++ b/src/modules/decklink/consumer_decklink.cpp @@ -25,13 +25,17 @@ #include #include #include +#include #ifdef WIN32 #include #include "DeckLinkAPI_h.h" #else #include "DeckLinkAPI.h" +typedef const char* BSTR; #endif +#define SAFE_RELEASE(V) if (V) { V->Release(); V = NULL; } + static const unsigned PREROLL_MINIMUM = 3; class DeckLinkConsumer @@ -58,12 +62,13 @@ private: uint32_t m_preroll; uint32_t m_acnt; bool m_reprio; + pthread_t m_prerollThread; IDeckLinkDisplayMode* getDisplayMode() { mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( getConsumer() ) ); - IDeckLinkDisplayModeIterator* iter; - IDeckLinkDisplayMode* mode; + IDeckLinkDisplayModeIterator* iter = NULL; + IDeckLinkDisplayMode* mode = NULL; IDeckLinkDisplayMode* result = 0; if ( m_deckLinkOutput->GetDisplayModeIterator( &iter ) == S_OK ) @@ -81,7 +86,10 @@ private: && m_fps == mlt_profile_fps( profile ) && ( m_height == profile->height || ( m_height == 486 && profile->height == 480 ) ) ) result = mode; + else + SAFE_RELEASE( mode ); } + SAFE_RELEASE( iter ); } return result; @@ -90,15 +98,21 @@ private: public: mlt_consumer getConsumer() { return &m_consumer; } - + + DeckLinkConsumer() + { + m_displayMode = NULL; + m_deckLinkKeyer = NULL; + m_deckLinkOutput = NULL; + m_deckLink = NULL; + } + ~DeckLinkConsumer() { - if ( m_deckLinkKeyer ) - m_deckLinkKeyer->Release(); - if ( m_deckLinkOutput ) - m_deckLinkOutput->Release(); - if ( m_deckLink ) - m_deckLink->Release(); + SAFE_RELEASE( m_displayMode ); + SAFE_RELEASE( m_deckLinkKeyer ); + SAFE_RELEASE( m_deckLinkOutput ); + SAFE_RELEASE( m_deckLink ); } bool open( unsigned card = 0 ) @@ -127,30 +141,32 @@ public: return false; } #endif - + // Connect to the Nth DeckLink instance - do { - if ( deckLinkIterator->Next( &m_deckLink ) != S_OK ) - { - mlt_log_error( getConsumer(), "DeckLink card not found\n" ); - deckLinkIterator->Release(); - return false; - } - } while ( ++i <= card ); - deckLinkIterator->Release(); - + for ( i = 0; deckLinkIterator->Next( &m_deckLink ) == S_OK ; i++) + { + if( i == card ) + break; + else + SAFE_RELEASE( m_deckLink ); + } + SAFE_RELEASE( deckLinkIterator ); + if ( !m_deckLink ) + { + mlt_log_error( getConsumer(), "DeckLink card not found\n" ); + return false; + } + // Obtain the audio/video output interface (IDeckLinkOutput) if ( m_deckLink->QueryInterface( IID_IDeckLinkOutput, (void**)&m_deckLinkOutput ) != S_OK ) { mlt_log_error( getConsumer(), "No DeckLink cards support output\n" ); - m_deckLink->Release(); - m_deckLink = 0; + SAFE_RELEASE( m_deckLink ); return false; } // Get the keyer interface IDeckLinkAttributes *deckLinkAttributes = 0; - m_deckLinkKeyer = 0; if ( m_deckLink->QueryInterface( IID_IDeckLinkAttributes, (void**) &deckLinkAttributes ) == S_OK ) { #ifdef WIN32 @@ -163,14 +179,12 @@ public: if ( m_deckLink->QueryInterface( IID_IDeckLinkKeyer, (void**) &m_deckLinkKeyer ) != S_OK ) { mlt_log_error( getConsumer(), "Failed to get keyer\n" ); - m_deckLinkOutput->Release(); - m_deckLinkOutput = 0; - m_deckLink->Release(); - m_deckLink = 0; + SAFE_RELEASE( m_deckLinkOutput ); + SAFE_RELEASE( m_deckLink ); return false; } } - deckLinkAttributes->Release(); + SAFE_RELEASE( deckLinkAttributes ); } // Provide this class as a delegate to the audio and video output interfaces @@ -178,10 +192,28 @@ public: return true; } - + + + void* preroll_thread() + { + // preroll frames + for ( unsigned i = 0; i < m_preroll; i++ ) + ScheduleNextFrame( true ); + + // start scheduled playback + m_deckLinkOutput->StartScheduledPlayback( 0, m_timescale, 1.0 ); + + return 0; + } + + static void* preroll_thread_proxy( void* arg ) + { + DeckLinkConsumer* self = static_cast< DeckLinkConsumer* >( arg ); + return self->preroll_thread(); + } + bool start( unsigned preroll ) { - unsigned i; mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() ); // Initialize members @@ -241,12 +273,8 @@ public: m_preroll = preroll; m_reprio = false; - // preroll frames - for( i = 0; i < preroll; i++ ) - ScheduleNextFrame( true ); - - // start scheduled playback - m_deckLinkOutput->StartScheduledPlayback( 0, m_timescale, 1.0 ); + // Do preroll in thread to ensure asynchronicity of mlt_consumer_start(). + pthread_create( &m_prerollThread, NULL, preroll_thread_proxy, this ); // Set the running state mlt_properties_set_int( properties, "running", 1 ); @@ -257,16 +285,12 @@ public: bool stop() { mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() ); + bool wasRunning = !!mlt_properties_get_int( properties, "running" ); // set running state is 0 mlt_properties_set_int( properties, "running", 0 ); mlt_consumer_stopped( getConsumer() ); - // release decklink frame - if ( m_decklinkFrame ) - m_decklinkFrame->Release(); - m_decklinkFrame = NULL; - // Stop the audio and video output streams immediately if ( m_deckLinkOutput ) { @@ -275,6 +299,12 @@ public: m_deckLinkOutput->DisableVideoOutput(); } + // release decklink frame + SAFE_RELEASE( m_decklinkFrame ); + + if ( wasRunning ) + pthread_join( m_prerollThread, NULL ); + return true; } @@ -359,8 +389,7 @@ public: uint8_t* buffer = 0; int stride = m_width * ( m_isKeyer? 4 : 2 ); - if ( m_decklinkFrame ) - m_decklinkFrame->Release(); + SAFE_RELEASE( m_decklinkFrame ); if ( createFrame( &m_decklinkFrame ) ) m_decklinkFrame->GetBytes( (void**) &buffer ); @@ -490,7 +519,7 @@ public: r = pthread_setschedparam(thread, SCHED_FIFO, ¶m); if( r ) mlt_log_verbose( getConsumer(), - "ScheduledFrameCompleted: pthread_setschedparam retured %d\n", r); + "ScheduledFrameCompleted: pthread_setschedparam returned %d\n", r); else mlt_log_verbose( getConsumer(), "ScheduledFrameCompleted: param.sched_priority=%d\n", param.sched_priority); @@ -507,7 +536,7 @@ public: m_deckLinkOutput->GetBufferedAudioSampleFrameCount( &cnt ); if ( cnt != m_acnt ) { - mlt_log_verbose( getConsumer(), + mlt_log_debug( getConsumer(), "ScheduledFrameCompleted: GetBufferedAudioSampleFrameCount %u -> %lu, m_count=%"PRIu64"\n", m_acnt, cnt, m_count ); m_acnt = cnt; @@ -623,6 +652,51 @@ static void close( mlt_consumer consumer ) 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* decklinkOutput = 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_IDeckLinkOutput, (void**) &decklinkOutput ) == S_OK ) + { + char *name = NULL; + if ( decklink->GetModelName( (BSTR*) &name ) == S_OK ) + { + const char *format = "device.%d"; + char *key = (char*) calloc( 1, strlen( format ) + 1 ); + + sprintf( key, format, i ); + mlt_properties_set( properties, key, name ); + free( key ); + free( name ); + } + SAFE_RELEASE( decklinkOutput ); + } + SAFE_RELEASE( decklink ); + } + SAFE_RELEASE( decklinkIterator ); + mlt_properties_set_int( properties, "devices", i ); +} + /** Initialise the consumer. */ @@ -639,13 +713,17 @@ mlt_consumer consumer_decklink_init( mlt_profile profile, mlt_service_type type, if ( decklink->open( arg? atoi(arg) : 0 ) ) { consumer = decklink->getConsumer(); - + mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer ); + // Setup callbacks consumer->close = close; consumer->start = start; consumer->stop = stop; consumer->is_stopped = is_stopped; - mlt_properties_set( MLT_CONSUMER_PROPERTIES(consumer), "deinterlace_method", "onefield" ); + mlt_properties_set( properties, "deinterlace_method", "onefield" ); + + 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 ); } }