X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmodules%2Fdecklink%2Fproducer_decklink.cpp;h=877c0eacc400742e3ea686e92eb3b2613b74f94e;hb=307a2ef3af84a5dba60a5026a1a24f430a0e0bf2;hp=6d327ab82dc8f23b62ef2db84cb5f18e7e2c8c6b;hpb=443c856ac388f077409ddb8076012f511fa10fc0;p=mlt diff --git a/src/modules/decklink/producer_decklink.cpp b/src/modules/decklink/producer_decklink.cpp index 6d327ab8..877c0eac 100644 --- a/src/modules/decklink/producer_decklink.cpp +++ b/src/modules/decklink/producer_decklink.cpp @@ -22,25 +22,34 @@ #include #include #include -#include "DeckLinkAPI.h" +#include +#include +#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; pthread_mutex_t m_mutex; pthread_cond_t m_condition; bool m_started; - - BMDDisplayMode getDisplayMode( mlt_profile profile ) + int m_dropped; + bool m_isBuffering; + int m_topFieldFirst; + int m_colorspace; + int m_vancLines; + mlt_cache m_cache; + bool m_reprio; + + 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 ) { @@ -53,12 +62,17 @@ private: mode->GetFrameRate( &duration, ×cale ); double fps = (double) timescale / duration; int p = mode->GetFieldDominance() == bmdProgressiveFrame; - mlt_log_verbose( getProducer(), "BMD mode %dx%d %.3f fps prog %d\n", width, height, fps, p ); + m_topFieldFirst = mode->GetFieldDominance() == bmdUpperFieldFirst; + m_colorspace = ( mode->GetFlags() & bmdDisplayModeColorspaceRec709 ) ? 709 : 601; + 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 && height == profile->height && p == profile->progressive - && fps == mlt_profile_fps( profile ) ) + if ( width == profile->width && p == profile->progressive + && ( 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; @@ -66,39 +80,61 @@ private: public: - mlt_producer getProducer() - { return &m_producer; } + void setProducer( mlt_producer producer ) + { m_producer = producer; } + + mlt_producer getProducer() const + { return m_producer; } - ~DeckLinkProducer() + 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 = 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 ) @@ -112,11 +148,17 @@ public: pthread_cond_init( &m_condition, NULL ); m_queue = mlt_deque_init(); 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; } @@ -129,17 +171,41 @@ 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; + SAFE_RELEASE( decklinkAttributes ); + } + mlt_log_verbose( getProducer(), "%s format detection\n", doesDetectFormat ? "supports" : "does not support" ); // Enable video capture BMDPixelFormat pixelFormat = bmdFormat8BitYUV; - BMDVideoInputFlags flags = bmdVideoInputFlagDefault; + BMDVideoInputFlags flags = doesDetectFormat ? bmdVideoInputEnableFormatDetection : bmdVideoInputFlagDefault; if ( S_OK != m_decklinkInput->EnableVideoInput( displayMode, pixelFormat, flags ) ) throw "Failed to enable video capture."; @@ -151,6 +217,8 @@ public: throw "Failed to enable audio capture."; // Start capture + m_dropped = 0; + mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "dropped", m_dropped ); m_started = m_decklinkInput->StartStreams() == S_OK; if ( !m_started ) throw "Failed to start capture."; @@ -166,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 ); @@ -175,42 +244,100 @@ 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 ) + { + int prefill = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "prefill" ); + int buffer = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "buffer" ); - // Wait if queue is empty - pthread_mutex_lock( &m_mutex ); - while ( mlt_deque_count( m_queue ) < 1 ) - pthread_cond_wait( &m_condition, &m_mutex ); + m_isBuffering = false; + prefill = prefill > buffer ? buffer : prefill; + pthread_mutex_lock( &m_mutex ); + while ( mlt_deque_count( m_queue ) < prefill ) + { + // Wait up to buffer/fps seconds + gettimeofday( &now, NULL ); + long usec = now.tv_sec * 1000000 + now.tv_usec; + usec += 1000000 * buffer / fps; + tm.tv_sec = usec / 1000000; + tm.tv_nsec = (usec % 1000000) * 1000; + if ( pthread_cond_timedwait( &m_condition, &m_mutex, &tm ) ) + break; + } + 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 ); + if ( !frame ) + { + // 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 ); + + // add to cache + if ( frame ) + { + mlt_frame_set_position( frame, position ); + mlt_cache_put_frame( m_cache, frame ); + } + } // Set frame timestamp and properties - mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( getProducer() ) ); - mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); - mlt_properties_set_int( properties, "progressive", profile->progressive ); - mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) ); - mlt_properties_set_int( properties, "width", profile->width ); - mlt_properties_set_int( properties, "real_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, "format", mlt_image_yuv422 ); - mlt_properties_set_int( properties, "audio_frequency", 48000 ); - mlt_properties_set_int( properties, "audio_channels", - mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "channels" ) ); + if ( frame ) + { + mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( getProducer() ) ); + mlt_properties properties = MLT_FRAME_PROPERTIES( frame ); + mlt_properties_set_int( properties, "progressive", profile->progressive ); + mlt_properties_set_int( properties, "meta.media.progressive", profile->progressive ); + mlt_properties_set_int( properties, "top_field_first", m_topFieldFirst ); + mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) ); + mlt_properties_set_int( properties, "meta.media.sample_aspect_num", profile->sample_aspect_num ); + mlt_properties_set_int( properties, "meta.media.sample_aspect_den", profile->sample_aspect_den ); + 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, "meta.media.width", profile->width ); + mlt_properties_set_int( properties, "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 ); + mlt_properties_set_int( properties, "meta.media.colorspace", m_colorspace ); + mlt_properties_set_int( properties, "audio_frequency", 48000 ); + 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; } @@ -231,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() ) ); @@ -239,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( buffer, 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 ) @@ -261,6 +460,23 @@ public: mlt_frame_close( frame ); frame = 0; } + + // Get timecode + IDeckLinkTimecode* timecode = 0; + if ( video->GetTimecode( bmdTimecodeVITC, &timecode ) == S_OK && timecode ) + { + DLString timecodeString = 0; + + if ( timecode->GetString( &timecodeString ) == S_OK ) + { + 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 ); + } + freeDLString( timecodeString ); + SAFE_RELEASE( timecode ); + } } else { @@ -307,7 +523,11 @@ public: pthread_cond_broadcast( &m_condition ); } else + { mlt_frame_close( frame ); + mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "dropped", ++m_dropped ); + mlt_log_warning( getProducer(), "buffer overrun, frame dropped %d\n", m_dropped ); + } pthread_mutex_unlock( &m_mutex ); } @@ -319,6 +539,56 @@ public: IDeckLinkDisplayMode* mode, BMDDetectedVideoInputFormatFlags flags ) { + mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( getProducer() ) ); + if ( events & bmdVideoInputDisplayModeChanged ) + { + BMDTimeValue duration; + BMDTimeScale timescale; + mode->GetFrameRate( &duration, ×cale ); + profile->width = mode->GetWidth(); + profile->height = mode->GetHeight() + m_vancLines; + profile->frame_rate_num = timescale; + profile->frame_rate_den = duration; + if ( profile->width == 720 ) + { + if ( profile->height == 576 ) + { + profile->sample_aspect_num = 16; + profile->sample_aspect_den = 15; + } + else + { + profile->sample_aspect_num = 8; + profile->sample_aspect_den = 9; + } + profile->display_aspect_num = 4; + profile->display_aspect_den = 3; + } + else + { + profile->sample_aspect_num = 1; + profile->sample_aspect_den = 1; + profile->display_aspect_num = 16; + profile->display_aspect_den = 9; + } + free( profile->description ); + profile->description = strdup( "decklink" ); + mlt_log_verbose( getProducer(), "format changed %dx%d %.3f fps\n", + profile->width, profile->height, (double) profile->frame_rate_num / profile->frame_rate_den ); + } + if ( events & bmdVideoInputFieldDominanceChanged ) + { + profile->progressive = mode->GetFieldDominance() == bmdProgressiveFrame; + m_topFieldFirst = mode->GetFieldDominance() == bmdUpperFieldFirst; + mlt_log_verbose( getProducer(), "field dominance changed prog %d tff %d\n", + profile->progressive, m_topFieldFirst ); + } + if ( events & bmdVideoInputColorspaceChanged ) + { + profile->colorspace = m_colorspace = + ( mode->GetFlags() & bmdDisplayModeColorspaceRec709 ) ? 709 : 601; + mlt_log_verbose( getProducer(), "colorspace changed %d\n", profile->colorspace ); + } return S_OK; } }; @@ -336,30 +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; - // Get the next frame from the decklink object - *frame = decklink->getFrame(); + // 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" ) ); + } + + // 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) ); // 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. */ @@ -367,30 +711,36 @@ 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( MLT_PRODUCER_PROPERTIES( producer ), "resource", arg ); - mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( producer ), "channels", 2 ); - mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( producer ), "buffer", 25 ); - - // Start immediately - if ( !decklink->start( profile ) ) - { - producer_close( producer ); - producer = NULL; - } + 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 ); + + // These properties effectively make it infinite. + 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 ); } }