X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmodules%2Fdecklink%2Fproducer_decklink.cpp;h=877c0eacc400742e3ea686e92eb3b2613b74f94e;hb=307a2ef3af84a5dba60a5026a1a24f430a0e0bf2;hp=58e9c9a2f01bf32ea6a55f0592f2447fe17ab323;hpb=14119031ede6619dfe78ac4b5909c4452de10bbe;p=mlt diff --git a/src/modules/decklink/producer_decklink.cpp b/src/modules/decklink/producer_decklink.cpp index 58e9c9a2..877c0eac 100644 --- a/src/modules/decklink/producer_decklink.cpp +++ b/src/modules/decklink/producer_decklink.cpp @@ -24,18 +24,13 @@ #include #include #include -#ifdef WIN32 -#include -#include "DeckLinkAPI_h.h" -#else -#include "DeckLinkAPI.h" -#endif +#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; @@ -46,11 +41,14 @@ private: 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; + IDeckLinkDisplayModeIterator* iter = NULL; + IDeckLinkDisplayMode* mode = NULL; BMDDisplayMode result = (BMDDisplayMode) bmdDisplayModeNotSupported; if ( m_decklinkInput->GetDisplayModeIterator( &iter ) == S_OK ) @@ -69,10 +67,12 @@ private: 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 && p == profile->progressive - && ( height == profile->height || ( height == 486 && profile->height == 480 ) ) - && fps == mlt_profile_fps( profile ) ) + && ( 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; @@ -80,25 +80,34 @@ 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 = NULL; try @@ -115,14 +124,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 ) @@ -138,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; } @@ -155,13 +171,22 @@ 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 ); + 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 @@ -174,7 +199,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" ); @@ -209,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 ); @@ -218,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 ) @@ -258,24 +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 ); - 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; - } + // 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 ) @@ -291,10 +326,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 ); @@ -303,6 +336,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; } @@ -322,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, ¶m); + + thread = pthread_self(); + + r = pthread_setschedparam(thread, SCHED_FIFO, ¶m); + 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() ) ); @@ -330,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( (char*) buffer, (char*) 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 ) @@ -357,16 +465,17 @@ public: IDeckLinkTimecode* timecode = 0; if ( video->GetTimecode( bmdTimecodeVITC, &timecode ) == S_OK && timecode ) { - const char* timecodeString = 0; + DLString timecodeString = 0; if ( timecode->GetString( &timecodeString ) == S_OK ) { - mlt_properties_set( MLT_FRAME_PROPERTIES( frame ), "meta.attr.vitc.markup", timecodeString ); - mlt_log_verbose( 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 @@ -417,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 ); } @@ -437,8 +546,7 @@ public: BMDTimeScale timescale; mode->GetFrameRate( &duration, ×cale ); profile->width = mode->GetWidth(); - profile->height = mode->GetHeight(); - profile->height = profile->height == 486 ? 480 : profile->height; + profile->height = mode->GetHeight() + m_vancLines; profile->frame_rate_num = timescale; profile->frame_rate_den = duration; if ( profile->width == 720 ) @@ -498,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; + + // 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" ) ); + } - // Get the next frame from the decklink object - *frame = decklink->getFrame(); + // 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. */ @@ -531,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 ); @@ -556,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 ); } }