]> git.sesse.net Git - mlt/blobdiff - src/modules/decklink/consumer_decklink.cpp
Fix compiler warnings due to non-virtual destructors.
[mlt] / src / modules / decklink / consumer_decklink.cpp
index 1d3eaab7e55c8be7bfede501e2c2cbbe3488dfe4..e6d20e09998373ac179cb0251c4a345aacf30f4c 100644 (file)
@@ -17,6 +17,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#define __STDC_FORMAT_MACROS  /* see inttypes.h */
 #include <framework/mlt.h>
 #include <stdlib.h>
 #include <string.h>
 #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;
 
@@ -54,14 +51,18 @@ private:
        int                         m_isKeyer;
        IDeckLinkKeyer*             m_deckLinkKeyer;
        bool                        m_terminate_on_pause;
+       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 )
@@ -72,30 +73,41 @@ private:
                                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;
@@ -115,37 +127,39 @@ public:
                }
 #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
@@ -158,25 +172,44 @@ public:
                                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
@@ -195,11 +228,11 @@ public:
                        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" ) ) )
                {
-                       bool external = (m_isKeyer == 2);
+                       bool external = ( m_isKeyer == 2 );
                        double level = mlt_properties_get_double( properties, "keyer_level" );
 
                        if ( m_deckLinkKeyer->Enable( external ) != S_OK )
@@ -213,7 +246,7 @@ public:
                }
 
                // Set the video output mode
-               if ( S_OK != m_deckLinkOutput->EnableVideoOutput( m_displayMode->GetDisplayMode(), bmdVideoOutputFlagDefault) )
+               if ( S_OK != m_deckLinkOutput->EnableVideoOutput( m_displayMode->GetDisplayMode(), bmdVideoOutputFlagDefault ) )
                {
                        mlt_log_error( getConsumer(), "Failed to enable video output\n" );
                        return false;
@@ -233,31 +266,28 @@ public:
                        return false;
                }
 
-               // preroll frames
-               for( i = 0; i < preroll; i++ )
-                       ScheduleNextFrame( true );
-
-               // start scheduled playback
-               m_deckLinkOutput->StartScheduledPlayback( 0, m_timescale, 1.0 );
+               m_preroll = preroll;
+               m_reprio = false;
 
                // 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 )
@@ -267,6 +297,11 @@ public:
                        m_deckLinkOutput->DisableVideoOutput();
                }
 
+               // release decklink frame
+               SAFE_RELEASE( m_decklinkFrame );
+
+               mlt_consumer_stopped( getConsumer() );
+
                return true;
        }
 
@@ -279,9 +314,20 @@ public:
 
                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 " DECKLINK_UNSIGNED_FORMAT " audiosamples\n", written );
+                               m_deckLinkOutput->FlushBufferedAudioSamples();
+                       };
 #ifdef WIN32
                        m_deckLinkOutput->ScheduleAudioSamples( pcm, samples, streamTime, frequency, (unsigned long*) &written );
 #else
@@ -289,7 +335,7 @@ public:
 #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 );
                }
        }
 
@@ -310,7 +356,7 @@ public:
                        stop();
                        return false;
                }
-               
+
                // Make the first line black for field order correction.
                if ( S_OK == frame->GetBytes( (void**) &buffer ) && buffer )
                {
@@ -335,14 +381,14 @@ public:
                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 );
 
@@ -350,26 +396,41 @@ public:
                        {
                                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;
                                        }
@@ -388,7 +449,7 @@ public:
                                else
                                {
                                        // Keying blank frames - nullify alpha
-                                       memset( buffer, 0, stride * m_height );
+                                       memset( buffer, 0, stride * height );
                                }
                        }
                }
@@ -414,7 +475,7 @@ public:
 
                return result;
        }
-       
+
        // *** DeckLink API implementation of IDeckLinkVideoOutputCallback IDeckLinkAudioOutputCallback *** //
 
        // IUnknown needs only a dummy implementation
@@ -424,29 +485,80 @@ public:
                { return 1; }
        virtual ULONG STDMETHODCALLTYPE Release()
                { return 1; }
-       
+
        /************************* DeckLink API Delegate Methods *****************************/
-       
+
        virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted( IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult completed )
        {
+               if( !m_reprio )
+               {
+                       mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
+
+                       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, &param);
+
+                               thread = pthread_self();
+
+                               r = pthread_setschedparam(thread, SCHED_FIFO, &param);
+                               if( r )
+                                       mlt_log_verbose( getConsumer(),
+                                               "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_debug( getConsumer(),
+                               "ScheduledFrameCompleted: GetBufferedAudioSampleFrameCount %u -> " DECKLINK_UNSIGNED_FORMAT
+                               ", m_count=%"PRIu64"\n", m_acnt, cnt, m_count );
+                       m_acnt = cnt;
+               }
+
                // When a video frame has been released by the API, schedule another video frame to be output
 
                // ignore handler if frame was flushed
-               if(bmdOutputFrameFlushed == completed)
+               if ( bmdOutputFrameFlushed == completed )
                        return S_OK;
 
                // schedule next frame
-               ScheduleNextFrame(false);
+               ScheduleNextFrame( false );
 
                // step forward frames counter if underrun
-               if(bmdOutputFrameDisplayedLate == completed)
+               if ( bmdOutputFrameDisplayedLate == completed )
                {
-                       mlt_log_verbose( getConsumer(), "ScheduledFrameCompleted: bmdOutputFrameDisplayedLate == completed\n");
+                       mlt_log_verbose( getConsumer(), "ScheduledFrameCompleted: bmdOutputFrameDisplayedLate == completed\n" );
                        m_count++;
                }
-               if(bmdOutputFrameDropped == completed)
+               if ( bmdOutputFrameDropped == completed )
                {
-                       mlt_log_verbose( getConsumer(), "ScheduledFrameCompleted: bmdOutputFrameDropped == completed\n");
+                       mlt_log_verbose( getConsumer(), "ScheduledFrameCompleted: bmdOutputFrameDropped == completed\n" );
                        m_count++;
                }
 
@@ -457,9 +569,9 @@ public:
        {
                return mlt_consumer_is_stopped( getConsumer() ) ? S_FALSE : S_OK;
        }
-       
 
-       void ScheduleNextFrame(bool preroll)
+
+       void ScheduleNextFrame( bool preroll )
        {
                // get the consumer
                mlt_consumer consumer = getConsumer();
@@ -480,8 +592,8 @@ public:
                                mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
 
                                // terminate on pause
-                               if (m_terminate_on_pause &&
-                                       mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0)
+                               if ( m_terminate_on_pause &&
+                                       mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0 )
                                        stop();
 
                                mlt_frame_close( frame );
@@ -539,6 +651,53 @@ static void close( mlt_consumer consumer )
 
 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.
  */
 
@@ -555,13 +714,17 @@ mlt_consumer consumer_decklink_init( mlt_profile profile, mlt_service_type type,
                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 );
                }
        }