X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fmodules%2Fdecklink%2Fproducer_decklink.cpp;h=be3406683401e6849496b20f794279708f458f38;hb=a76d1b9744bfd10d252f598f44f523afda11d1d9;hp=5c81d9f83fd616651093b4c91cff039b8e347ece;hpb=3cf855b0c56141878af553ad77c674175d821179;p=mlt diff --git a/src/modules/decklink/producer_decklink.cpp b/src/modules/decklink/producer_decklink.cpp index 5c81d9f8..be340668 100644 --- a/src/modules/decklink/producer_decklink.cpp +++ b/src/modules/decklink/producer_decklink.cpp @@ -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 );