#include <unistd.h>
#include <sys/time.h>
#include <limits.h>
-#ifdef WIN32
-#include <objbase.h>
-#include "DeckLinkAPI_h.h"
-#else
-#include "DeckLinkAPI.h"
-#endif
+#include <pthread.h>
+#include "common.h"
static const unsigned PREROLL_MINIMUM = 3;
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 )
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;
}
#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
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
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" ) ) )
{
m_preroll = preroll;
m_reprio = false;
- // preroll frames
- for( i = 0; i < preroll; i++ )
- ScheduleNextFrame( true );
-
- // start scheduled playback
- m_deckLinkOutput->StartScheduledPlayback( 0, m_timescale, 1.0 );
-
// 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 )
m_deckLinkOutput->DisableVideoOutput();
}
+ // release decklink frame
+ SAFE_RELEASE( m_decklinkFrame );
+
+ mlt_consumer_stopped( getConsumer() );
+
return true;
}
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
#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 );
}
}
stop();
return false;
}
-
+
// Make the first line black for field order correction.
if ( S_OK == frame->GetBytes( (void**) &buffer ) && buffer )
{
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 );
{
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;
}
else
{
// Keying blank frames - nullify alpha
- memset( buffer, 0, stride * m_height );
+ memset( buffer, 0, stride * height );
}
}
}
return result;
}
-
+
// *** DeckLink API implementation of IDeckLinkVideoOutputCallback IDeckLinkAudioOutputCallback *** //
// IUnknown needs only a dummy implementation
{ return 1; }
virtual ULONG STDMETHODCALLTYPE Release()
{ return 1; }
-
+
/************************* DeckLink API Delegate Methods *****************************/
-
+
virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted( IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult completed )
{
if( !m_reprio )
r = pthread_setschedparam(thread, SCHED_FIFO, ¶m);
if( r )
mlt_log_verbose( getConsumer(),
- "ScheduledFrameCompleted: pthread_setschedparam retured %d\n", r);
+ "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;
}
{
return mlt_consumer_is_stopped( getConsumer() ) ? S_FALSE : S_OK;
}
-
+
void ScheduleNextFrame( bool preroll )
{
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.
*/
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 );
}
}