X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmodules%2Fdecklink%2Fproducer_decklink.cpp;h=93489091d8dc46955eceaf8ed8e727b3f4150e85;hb=edce14a421c21c1a190ade2d16d74753fa0c0511;hp=710ef56203ea33fb1325da1e609cc92dad9732cc;hpb=66f934d75035583098745fd8b3472b550739c693;p=mlt diff --git a/src/modules/decklink/producer_decklink.cpp b/src/modules/decklink/producer_decklink.cpp index 710ef562..93489091 100644 --- a/src/modules/decklink/producer_decklink.cpp +++ b/src/modules/decklink/producer_decklink.cpp @@ -24,13 +24,20 @@ #include #include #include +#ifdef WIN32 +#include +#include "DeckLinkAPI_h.h" +#else #include "DeckLinkAPI.h" +#endif + +#define SAFE_RELEASE(V) if (V) { V->Release(); V = NULL; } class DeckLinkProducer : public IDeckLinkInputCallback { private: - mlt_producer_s m_producer; + mlt_producer m_producer; IDeckLink* m_decklink; IDeckLinkInput* m_decklinkInput; mlt_deque m_queue; @@ -41,12 +48,14 @@ private: bool m_isBuffering; int m_topFieldFirst; int m_colorspace; + int m_vancLines; + mlt_cache m_cache; - 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 ) { @@ -64,10 +73,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 ) ) + && ( 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; @@ -75,39 +86,110 @@ private: public: - mlt_producer getProducer() - { return &m_producer; } + void setProducer( mlt_producer producer ) + { m_producer = producer; } + + 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 listDevices( mlt_properties properties ) + { + IDeckLinkIterator* decklinkIterator = NULL; + try + { + int i = 0; +#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 + for ( ; decklinkIterator->Next( &m_decklink ) == S_OK; i++ ) + { + if ( m_decklink->QueryInterface( IID_IDeckLinkInput, (void**) &m_decklinkInput ) == S_OK ) + { + char *name = NULL; + if ( m_decklink->GetModelName( (const char**) &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( m_decklinkInput ); + } + SAFE_RELEASE( m_decklink ); + } + SAFE_RELEASE( decklinkIterator ); + mlt_properties_set_int( properties, "devices", i ); + return true; + } + catch ( const char *error ) + { + SAFE_RELEASE( decklinkIterator ); + mlt_log_error( getProducer(), "%s\n", error ); + return false; } } - 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 ) @@ -123,11 +205,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; } @@ -140,22 +226,35 @@ 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; - decklinkAttributes->Release(); + SAFE_RELEASE( decklinkAttributes ); } mlt_log_verbose( getProducer(), "%s format detection\n", doesDetectFormat ? "supports" : "does not support" ); @@ -190,8 +289,9 @@ public: void stop() { - if ( m_started ) + if ( !m_started ) return; + m_started = false; // Release the wait in getFrame pthread_mutex_lock( &m_mutex ); @@ -205,8 +305,6 @@ public: 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() @@ -215,6 +313,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 ) @@ -239,24 +339,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 ) @@ -272,10 +384,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 ); @@ -284,6 +394,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; } @@ -303,6 +416,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() ) ); @@ -311,14 +431,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 ) + swab( (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(); + swab( (char*) buffer, (char*) image + m_vancLines * video->GetRowBytes(), size ); mlt_frame_set_image( frame, (uint8_t*) image, size, mlt_pool_release ); } else if ( image ) @@ -333,6 +481,26 @@ public: mlt_frame_close( frame ); frame = 0; } + + // Get timecode + IDeckLinkTimecode* timecode = 0; + if ( video->GetTimecode( bmdTimecodeVITC, &timecode ) == S_OK && timecode ) + { + const char* 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 ); + } + if ( timecodeString ) + free( (void*) timecodeString ); + SAFE_RELEASE( timecode ); + } } else { @@ -402,8 +570,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 ) @@ -463,28 +630,54 @@ 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" { @@ -496,22 +689,26 @@ 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->listDevices( MLT_PRODUCER_PROPERTIES( producer ) ) && + 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 ); @@ -520,13 +717,6 @@ 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" ); - - // Start immediately - if ( !decklink->start( profile ) ) - { - producer_close( producer ); - producer = NULL; - } } }