#include <pthread.h>
#include <string.h>
#include <unistd.h>
-#include "DeckLinkAPI.h"
+#include <limits.h>
+#include <sys/time.h>
+#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 )
{
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;
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 )
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;
}
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.";
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.";
void stop()
{
- if ( m_started )
+ if ( !m_started )
return;
+ m_started = false;
// Release the wait in getFrame
pthread_mutex_lock( &m_mutex );
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;
}
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() ) );
{
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 )
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
{
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 );
}
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;
}
};
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.
*/
{
// 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 );
}
}