X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmodules%2Fdecklink%2Fconsumer_decklink.cpp;h=e6d20e09998373ac179cb0251c4a345aacf30f4c;hb=307a2ef3af84a5dba60a5026a1a24f430a0e0bf2;hp=88c1cb977ecf11b9a726e37beb718d0a59a919a0;hpb=6e637be7158ce4481d1023b72ec48bce5bdad503;p=mlt diff --git a/src/modules/decklink/consumer_decklink.cpp b/src/modules/decklink/consumer_decklink.cpp index 88c1cb97..e6d20e09 100644 --- a/src/modules/decklink/consumer_decklink.cpp +++ b/src/modules/decklink/consumer_decklink.cpp @@ -25,12 +25,8 @@ #include #include #include -#ifdef WIN32 -#include -#include "DeckLinkAPI_h.h" -#else -#include "DeckLinkAPI.h" -#endif +#include +#include "common.h" static const unsigned PREROLL_MINIMUM = 3; @@ -57,14 +53,16 @@ private: bool m_terminate_on_pause; 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 ) { while ( !result && iter->Next( &mode ) == S_OK ) @@ -75,30 +73,41 @@ private: m_fps = (double) m_timescale / m_duration; int p = mode->GetFieldDominance() == bmdProgressiveFrame; mlt_log_verbose( getConsumer(), "BMD mode %dx%d %.3f fps prog %d\n", m_width, m_height, m_fps, p ); - - if ( m_width == profile->width && m_height == profile->height && p == profile->progressive - && m_fps == mlt_profile_fps( profile ) ) + + if ( m_width == profile->width && p == profile->progressive + && (int) m_fps == (int) 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; } - + public: mlt_consumer getConsumer() { return &m_consumer; } - - ~DeckLinkConsumer() + + DeckLinkConsumer() { - if ( m_deckLinkKeyer ) - m_deckLinkKeyer->Release(); - if ( m_deckLinkOutput ) - m_deckLinkOutput->Release(); - if ( m_deckLink ) - m_deckLink->Release(); + m_displayMode = NULL; + m_deckLinkKeyer = NULL; + m_deckLinkOutput = NULL; + m_deckLink = NULL; + m_decklinkFrame = NULL; + } + + virtual ~DeckLinkConsumer() + { + SAFE_RELEASE( m_displayMode ); + SAFE_RELEASE( m_deckLinkKeyer ); + SAFE_RELEASE( m_deckLinkOutput ); + SAFE_RELEASE( m_deckLink ); } - + bool open( unsigned card = 0 ) { unsigned i = 0; @@ -118,37 +127,39 @@ public: } #else IDeckLinkIterator* deckLinkIterator = CreateDeckLinkIteratorInstance(); - + if ( !deckLinkIterator ) { mlt_log_error( getConsumer(), "The DeckLink drivers not installed.\n" ); 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 @@ -161,25 +172,44 @@ 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 m_deckLinkOutput->SetScheduledFrameCompletionCallback( this ); - + return true; } - + + + void* preroll_thread() + { + mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() ); + + // preroll frames + for ( unsigned i = 0; i < m_preroll && mlt_properties_get_int( properties, "running" ); i++ ) + ScheduleNextFrame( true ); + + // start scheduled playback + if ( mlt_properties_get_int( properties, "running" ) ) + 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 @@ -198,7 +228,7 @@ public: mlt_log_error( getConsumer(), "Profile is not compatible with decklink.\n" ); return false; } - + // Set the keyer if ( m_deckLinkKeyer && ( m_isKeyer = mlt_properties_get_int( properties, "keyer" ) ) ) { @@ -237,32 +267,27 @@ public: } m_preroll = preroll; - - // preroll frames - for( i = 0; i < preroll; i++ ) - ScheduleNextFrame( true ); - - // start scheduled playback - m_deckLinkOutput->StartScheduledPlayback( 0, m_timescale, 1.0 ); + m_reprio = false; // Set the running state mlt_properties_set_int( properties, "running", 1 ); + // Do preroll in thread to ensure asynchronicity of mlt_consumer_start(). + pthread_create( &m_prerollThread, NULL, preroll_thread_proxy, this ); + return true; } - + 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; + if ( wasRunning ) + pthread_join( m_prerollThread, NULL ); // Stop the audio and video output streams immediately if ( m_deckLinkOutput ) @@ -272,6 +297,11 @@ public: m_deckLinkOutput->DisableVideoOutput(); } + // release decklink frame + SAFE_RELEASE( m_decklinkFrame ); + + mlt_consumer_stopped( getConsumer() ); + return true; } @@ -284,12 +314,18 @@ public: if ( !mlt_frame_get_audio( frame, (void**) &pcm, &format, &frequency, &m_channels, &samples ) ) { +#ifdef WIN32 +#define DECKLINK_UNSIGNED_FORMAT "%lu" + unsigned long written = 0; +#else +#define DECKLINK_UNSIGNED_FORMAT "%u" uint32_t written = 0; +#endif BMDTimeValue streamTime = m_count * frequency * m_duration / m_timescale; m_deckLinkOutput->GetBufferedAudioSampleFrameCount( &written ); if ( written > (m_preroll + 1) * samples ) { - mlt_log_verbose( getConsumer(), "renderAudio: will flush %d audiosamples\n", written ); + mlt_log_verbose( getConsumer(), "renderAudio: will flush " DECKLINK_UNSIGNED_FORMAT " audiosamples\n", written ); m_deckLinkOutput->FlushBufferedAudioSamples(); }; #ifdef WIN32 @@ -299,7 +335,7 @@ public: #endif if ( written != (uint32_t) samples ) - mlt_log_verbose( getConsumer(), "renderAudio: samples=%d, written=%u\n", samples, written ); + mlt_log_verbose( getConsumer(), "renderAudio: samples=%d, written=" DECKLINK_UNSIGNED_FORMAT "\n", samples, written ); } } @@ -320,7 +356,7 @@ public: stop(); return false; } - + // Make the first line black for field order correction. if ( S_OK == frame->GetBytes( (void**) &buffer ) && buffer ) { @@ -345,14 +381,14 @@ public: mlt_image_format format = m_isKeyer? mlt_image_rgb24a : mlt_image_yuv422; uint8_t* image = 0; int rendered = mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "rendered"); + int height = m_height; - if ( rendered && !mlt_frame_get_image( frame, &image, &format, &m_width, &m_height, 0 ) ) + if ( rendered && !mlt_frame_get_image( frame, &image, &format, &m_width, &height, 0 ) ) { 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 ); @@ -360,26 +396,41 @@ public: { int progressive = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "progressive" ); + // NTSC SDI is always 486 lines + if ( m_height == 486 && height == 480 ) + { + // blank first 6 lines + if ( m_isKeyer ) + { + memset( buffer, 0, stride * 6 ); + buffer += stride * 6; + } + else for ( int i = 0; i < m_width * 6; i++ ) + { + *buffer++ = 128; + *buffer++ = 16; + } + } if ( !m_isKeyer ) { // Normal non-keyer playout - needs byte swapping if ( !progressive && m_displayMode->GetFieldDominance() == bmdUpperFieldFirst ) // convert lower field first to top field first - swab( (char*) image, (char*) buffer + stride, stride * ( m_height - 1 ) ); + swab2( (char*) image, (char*) buffer + stride, stride * ( height - 1 ) ); else - swab( (char*) image, (char*) buffer, stride * m_height ); + swab2( (char*) image, (char*) buffer, stride * height ); } else if ( !mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "test_image" ) ) { // Normal keyer output - int y = m_height + 1; + int y = height + 1; uint32_t* s = (uint32_t*) image; uint32_t* d = (uint32_t*) buffer; if ( !progressive && m_displayMode->GetFieldDominance() == bmdUpperFieldFirst ) { // Correct field order - m_height--; + height--; y--; d += m_width; } @@ -398,7 +449,7 @@ public: else { // Keying blank frames - nullify alpha - memset( buffer, 0, stride * m_height ); + memset( buffer, 0, stride * height ); } } } @@ -424,7 +475,7 @@ public: return result; } - + // *** DeckLink API implementation of IDeckLinkVideoOutputCallback IDeckLinkAudioOutputCallback *** // // IUnknown needs only a dummy implementation @@ -434,18 +485,59 @@ public: { return 1; } virtual ULONG STDMETHODCALLTYPE Release() { return 1; } - + /************************* DeckLink API Delegate Methods *****************************/ - + virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted( IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult completed ) { + if( !m_reprio ) + { + mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() ); + + 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, ¶m); + + thread = pthread_self(); + + r = pthread_setschedparam(thread, SCHED_FIFO, ¶m); + if( r ) + mlt_log_verbose( getConsumer(), + "ScheduledFrameCompleted: pthread_setschedparam returned %d\n", r); + else + mlt_log_verbose( getConsumer(), + "ScheduledFrameCompleted: param.sched_priority=%d\n", param.sched_priority); + }; + + m_reprio = true; + }; + +#ifdef WIN32 + unsigned long cnt; +#else uint32_t cnt; +#endif m_deckLinkOutput->GetBufferedAudioSampleFrameCount( &cnt ); if ( cnt != m_acnt ) { - mlt_log_verbose( getConsumer(), - "ScheduledFrameCompleted: GetBufferedAudioSampleFrameCount %u -> %u, m_count=%"PRIu64"\n", - m_acnt, cnt, m_count ); + mlt_log_debug( getConsumer(), + "ScheduledFrameCompleted: GetBufferedAudioSampleFrameCount %u -> " DECKLINK_UNSIGNED_FORMAT + ", m_count=%"PRIu64"\n", m_acnt, cnt, m_count ); m_acnt = cnt; } @@ -477,7 +569,7 @@ public: { return mlt_consumer_is_stopped( getConsumer() ) ? S_FALSE : S_OK; } - + void ScheduleNextFrame( bool preroll ) { @@ -559,6 +651,53 @@ 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 ) + { + 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( decklinkOutput ); + } + SAFE_RELEASE( decklink ); + } + SAFE_RELEASE( decklinkIterator ); + mlt_properties_set_int( properties, "devices", i ); +} + /** Initialise the consumer. */ @@ -575,13 +714,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 ); } }