]> git.sesse.net Git - mlt/blobdiff - src/modules/decklink/producer_decklink.cpp
allow start decklink producer from pause
[mlt] / src / modules / decklink / producer_decklink.cpp
index 5c81d9f83fd616651093b4c91cff039b8e347ece..be3406683401e6849496b20f794279708f458f38 100644 (file)
@@ -35,7 +35,7 @@ class DeckLinkProducer
        : public IDeckLinkInputCallback
 {
 private:
-       mlt_producer_s   m_producer;
+       mlt_producer     m_producer;
        IDeckLink*       m_decklink;
        IDeckLinkInput*  m_decklinkInput;
        mlt_deque        m_queue;
@@ -47,6 +47,7 @@ private:
        int              m_topFieldFirst;
        int              m_colorspace;
        int              m_vancLines;
+       mlt_cache        m_cache;
 
        BMDDisplayMode getDisplayMode( mlt_profile profile, int vancLines )
        {
@@ -81,25 +82,29 @@ private:
 
 public:
 
-       mlt_producer getProducer()
-               { return &m_producer; }
+       void setProducer( mlt_producer producer )
+               { m_producer = producer; }
+
+       mlt_producer getProducer() const
+               { return m_producer; }
 
        ~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 );
                }
+               if ( m_decklinkInput )
+                       m_decklinkInput->Release();
+               if ( m_decklink )
+                       m_decklink->Release();
        }
 
-       bool open( mlt_profile profile, unsigned card =  0 )
+       bool open( unsigned card =  0 )
        {
                IDeckLinkIterator* decklinkIterator = NULL;
                try
@@ -139,6 +144,10 @@ 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 )
                {
@@ -167,7 +176,11 @@ public:
                        // Get the display mode
                        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
@@ -239,6 +252,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 )
@@ -263,24 +278,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 )
@@ -308,6 +335,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;
        }
 
@@ -327,6 +357,12 @@ public:
                        IDeckLinkVideoInputFrame* video,
                        IDeckLinkAudioInputPacket* audio )
        {
+               if ( 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() ) );
 
@@ -534,31 +570,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" ) );
+       }
 
        // Start if needed
-       decklink->start( mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ) );
+       if ( decklink )
+       {
+               decklink->start( mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ) );
 
-       // Get the next frame from the decklink object
-       *frame = decklink->getFrame();
+               // 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" {
@@ -570,22 +629,25 @@ 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( 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 );