]> git.sesse.net Git - mlt/blob - src/modules/decklink/consumer_decklink.cpp
Improve frame-dropping in decklink.
[mlt] / src / modules / decklink / consumer_decklink.cpp
1 /*
2  * consumer_decklink.c -- output through Blackmagic Design DeckLink
3  * Copyright (C) 2010 Dan Dennedy <dan@dennedy.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with consumer library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #include <framework/mlt.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <pthread.h>
24 #include <unistd.h>
25 #include <sys/time.h>
26 #include "DeckLinkAPI.h"
27
28 const unsigned PREROLL_MINIMUM = 3;
29
30 typedef struct
31 {
32         int16_t *buffer;
33         int size;
34         int used;
35         pthread_mutex_t mutex;
36 } *sample_fifo;
37
38 static sample_fifo sample_fifo_init()
39 {
40         sample_fifo fifo = (sample_fifo) calloc( 1, sizeof( *fifo ) );
41         pthread_mutex_init( &fifo->mutex, NULL );
42         return fifo;
43 }
44
45 static void sample_fifo_append( sample_fifo fifo, int16_t *samples, int count )
46 {
47         pthread_mutex_lock( &fifo->mutex );
48         if ( ( fifo->size - fifo->used ) < count )
49         {
50                 fifo->size += count * 5;
51                 fifo->buffer = (int16_t*) realloc( fifo->buffer, fifo->size * sizeof( int16_t ) );
52         }
53         memcpy( fifo->buffer + fifo->used, samples, count * sizeof( int16_t ) );
54         fifo->used += count;
55         pthread_mutex_unlock( &fifo->mutex );
56 }
57
58 static void sample_fifo_remove( sample_fifo fifo, int count )
59 {
60         pthread_mutex_lock( &fifo->mutex );
61         if ( count > fifo->used )
62                 count = fifo->used;
63         fifo->used -= count;
64         memmove( fifo->buffer, fifo->buffer + count, fifo->used * sizeof( int16_t ) );
65         pthread_mutex_unlock( &fifo->mutex );
66 }
67
68 static void sample_fifo_close( sample_fifo fifo )
69 {
70         free( fifo->buffer );
71         pthread_mutex_destroy( &fifo->mutex );
72         free( fifo );
73 }
74
75
76 class DeckLinkConsumer
77         : public IDeckLinkVideoOutputCallback
78         , public IDeckLinkAudioOutputCallback
79 {
80 private:
81         mlt_consumer_s              m_consumer;
82         IDeckLink*                  m_deckLink;
83         IDeckLinkOutput*            m_deckLinkOutput;
84         IDeckLinkMutableVideoFrame* m_videoFrame;
85         IDeckLinkDisplayMode*       m_displayMode;
86         pthread_mutex_t             m_mutex;
87         pthread_cond_t              m_condition;
88         int                         m_width;
89         int                         m_height;
90         BMDTimeValue                m_duration;
91         BMDTimeScale                m_timescale;
92         double                      m_fps;
93         uint64_t                    m_count;
94         sample_fifo                 m_fifo;
95         unsigned                    m_preroll;
96         bool                        m_isPrerolling;
97         unsigned                    m_prerollCounter;
98         int                         m_channels;
99         uint32_t                    m_maxAudioBuffer;
100         mlt_deque                   m_videoFrameQ;
101         mlt_frame                   m_frame;
102         unsigned                    m_dropped;
103                 
104         IDeckLinkDisplayMode* getDisplayMode()
105         {
106                 mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( &m_consumer ) );
107                 IDeckLinkDisplayModeIterator* iter;
108                 IDeckLinkDisplayMode* mode;
109                 IDeckLinkDisplayMode* result = 0;
110                 
111                 if ( m_deckLinkOutput->GetDisplayModeIterator( &iter ) == S_OK )
112                 {
113                         while ( !result && iter->Next( &mode ) == S_OK )
114                         {
115                                 m_width = mode->GetWidth();
116                                 m_height = mode->GetHeight();
117                                 mode->GetFrameRate( &m_duration, &m_timescale );
118                                 m_fps = (double) m_timescale / m_duration;
119                                 int p = mode->GetFieldDominance() == bmdProgressiveFrame;
120                                 mlt_log_verbose( &m_consumer, "BMD mode %dx%d %.3f fps prog %d\n", m_width, m_height, m_fps, p );
121                                 
122                                 if ( m_width == profile->width && m_height == profile->height && p == profile->progressive
123                                          && m_fps == mlt_profile_fps( profile ) )
124                                         result = mode;
125                         }
126                 }
127                 
128                 return result;
129         }
130         
131 public:
132         mlt_consumer getConsumer()
133                 { return &m_consumer; }
134         uint64_t isBuffering() const
135                 { return m_prerollCounter < m_preroll; }
136         
137         ~DeckLinkConsumer()
138         {
139                 if ( m_deckLinkOutput )
140                         m_deckLinkOutput->Release();
141                 if ( m_deckLink )
142                         m_deckLink->Release();
143                 if ( m_videoFrameQ )
144                         mlt_deque_close( m_videoFrameQ );
145         }
146         
147         bool open( unsigned card = 0 )
148         {
149                 IDeckLinkIterator* deckLinkIterator = CreateDeckLinkIteratorInstance();
150                 unsigned i = 0;
151                 
152                 if ( !deckLinkIterator )
153                 {
154                         mlt_log_verbose( NULL, "The DeckLink drivers not installed.\n" );
155                         return false;
156                 }
157                 
158                 // Connect to the Nth DeckLink instance
159                 do {
160                         if ( deckLinkIterator->Next( &m_deckLink ) != S_OK )
161                         {
162                                 mlt_log_verbose( NULL, "DeckLink card not found\n" );
163                                 deckLinkIterator->Release();
164                                 return false;
165                         }
166                 } while ( ++i <= card );
167                 
168                 // Obtain the audio/video output interface (IDeckLinkOutput)
169                 if ( m_deckLink->QueryInterface( IID_IDeckLinkOutput, (void**)&m_deckLinkOutput ) != S_OK )
170                 {
171                         mlt_log_verbose( NULL, "No DeckLink cards support output\n" );
172                         m_deckLink->Release();
173                         m_deckLink = 0;
174                         deckLinkIterator->Release();
175                         return false;
176                 }
177                 
178                 // Provide this class as a delegate to the audio and video output interfaces
179                 m_deckLinkOutput->SetScheduledFrameCompletionCallback( this );
180                 m_deckLinkOutput->SetAudioCallback( this );
181                 
182                 pthread_mutex_init( &m_mutex, NULL );
183                 pthread_cond_init( &m_condition, NULL );
184                 m_maxAudioBuffer = bmdAudioSampleRate48kHz;
185                 m_videoFrameQ = mlt_deque_init();
186                 
187                 return true;
188         }
189         
190         bool start( unsigned preroll )
191         {
192                 m_displayMode = getDisplayMode();
193                 if ( !m_displayMode )
194                 {
195                         mlt_log_error( &m_consumer, "Profile is not compatible with decklink.\n" );
196                         return false;
197                 }
198                 
199                 // Set the video output mode
200                 if ( S_OK != m_deckLinkOutput->EnableVideoOutput( m_displayMode->GetDisplayMode(), bmdVideoOutputFlagDefault) )
201                 {
202                         mlt_log_error( &m_consumer, "Failed to enable video output\n" );
203                         return false;
204                 }
205                 
206                 // Set the audio output mode
207                 m_channels = 2;
208                 if ( S_OK != m_deckLinkOutput->EnableAudioOutput( bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger,
209                         m_channels, bmdAudioOutputStreamContinuous ) )
210                 {
211                         mlt_log_error( &m_consumer, "Failed to enable audio output\n" );
212                         stop();
213                         return false;
214                 }
215                 m_fifo = sample_fifo_init();
216                 
217                 // Preroll
218                 m_isPrerolling = true;
219                 m_prerollCounter = 0;
220                 m_preroll = preroll < PREROLL_MINIMUM ? PREROLL_MINIMUM : preroll;
221                 m_count = 0;
222                 m_deckLinkOutput->BeginAudioPreroll();
223                 m_frame = 0;
224                 m_dropped = 0;
225                 
226                 return true;
227         }
228         
229         void wakeup()
230         {
231                 pthread_mutex_lock( &m_mutex );
232                 pthread_cond_broadcast( &m_condition );
233                 pthread_mutex_unlock( &m_mutex );
234         }
235         
236         void wait()
237         {
238                 struct timeval tv;
239                 struct timespec ts;
240                 
241                 gettimeofday( &tv, NULL );
242                 ts.tv_sec = tv.tv_sec + 1;
243                 ts.tv_nsec = tv.tv_usec * 1000;
244                 pthread_mutex_lock( &m_mutex );
245                 pthread_cond_timedwait( &m_condition, &m_mutex, &ts );
246                 pthread_mutex_unlock( &m_mutex );
247         }
248         
249         void stop()
250         {
251                 // Stop the audio and video output streams immediately
252                 if ( m_deckLinkOutput )
253                 {
254                         m_deckLinkOutput->StopScheduledPlayback( 0, 0, 0 );
255                         m_deckLinkOutput->DisableAudioOutput();
256                         m_deckLinkOutput->DisableVideoOutput();
257                 }
258                 while ( mlt_deque_count( m_videoFrameQ ) )
259                 {
260                         m_videoFrame = (IDeckLinkMutableVideoFrame*) mlt_deque_pop_back( m_videoFrameQ );
261                         m_videoFrame->Release();
262                 }
263                 m_videoFrame = 0;
264                 if ( m_fifo ) sample_fifo_close( m_fifo );
265                 mlt_frame_close( m_frame );
266         }
267         
268         void createFrame()
269         {
270                 m_videoFrame = 0;
271                 // Generate a DeckLink video frame
272                 if ( S_OK != m_deckLinkOutput->CreateVideoFrame( m_width, m_height,
273                         m_width * 2, bmdFormat8BitYUV, bmdFrameFlagDefault, &m_videoFrame ) )
274                 {
275                         mlt_log_verbose( &m_consumer, "Failed to create video frame\n" );
276                         stop();
277                         return;
278                 }
279                 
280                 // Make the first line black for field order correction.
281                 uint8_t *buffer = 0;
282                 if ( S_OK == m_videoFrame->GetBytes( (void**) &buffer ) && buffer )
283                 {
284                         for ( int i = 0; i < m_width; i++ )
285                         {
286                                 *buffer++ = 128;
287                                 *buffer++ = 16;
288                         }
289                 }
290                 mlt_log_debug( &m_consumer, "created video frame\n" );
291                 mlt_deque_push_back( m_videoFrameQ, m_videoFrame );
292         }
293
294         HRESULT render( mlt_frame frame )
295         {
296                 HRESULT result = S_OK;
297                 // Get the audio                
298                 double speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES(frame), "_speed" );
299                 if ( speed == 1.0 )
300                 {
301                         mlt_audio_format format = mlt_audio_s16;
302                         int frequency = bmdAudioSampleRate48kHz;
303                         int samples = mlt_sample_calculator( m_fps, frequency, m_count );
304                         int16_t *pcm = 0;
305                         
306                         if ( !mlt_frame_get_audio( frame, (void**) &pcm, &format, &frequency, &m_channels, &samples ) )
307                         {
308                                 int count = samples;
309                                 
310                                 if ( !m_isPrerolling )
311                                 {
312                                         uint32_t audioCount = 0;
313                                         uint32_t videoCount = 0;
314                                         
315                                         // Check for resync
316                                         m_deckLinkOutput->GetBufferedAudioSampleFrameCount( &audioCount );
317                                         m_deckLinkOutput->GetBufferedVideoFrameCount( &videoCount );
318                                         
319                                         // Underflow typically occurs during non-normal speed playback.
320                                         if ( audioCount < 1 || videoCount < 1 )
321                                         {
322                                                 // Upon switching to normal playback, buffer some frames faster than realtime.
323                                                 mlt_log_info( &m_consumer, "buffer underrun: audio buf %u video buf %u frames\n", audioCount, videoCount );
324                                                 m_prerollCounter = 0;
325                                         }
326                                         
327                                         // While rebuffering
328                                         if ( isBuffering() )
329                                         {
330                                                 // Only append audio to reach the ideal level and not overbuffer.
331                                                 int ideal = ( m_preroll - 1 ) * bmdAudioSampleRate48kHz / m_fps;
332                                                 int actual = m_fifo->used / m_channels + audioCount;
333                                                 int diff = ideal / 2 - actual;
334                                                 count = diff < 0 ? 0 : diff < count ? diff : count;
335                                         }
336                                 }
337                                 if ( count > 0 )
338                                         sample_fifo_append( m_fifo, pcm, count * m_channels );
339                         }
340                 }
341                 
342                 // Create video frames while pre-rolling
343                 if ( m_isPrerolling )
344                 {
345                         createFrame();
346                         if ( !m_videoFrame )
347                         {
348                                 mlt_log_error( &m_consumer, "failed to create video frame\n" );
349                                 return S_FALSE;
350                         }
351                 }
352                 
353                 if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "rendered") )
354                 {
355                         // Close the previous frame and use the new one
356                         mlt_frame_close( m_frame );
357                         m_frame = frame;
358                 }
359                 else
360                 {
361                         // Reuse the last frame
362                         mlt_log_verbose( &m_consumer, "dropped video frame %u\n", ++m_dropped );
363                 }
364
365                 // Get the video
366                 mlt_image_format format = mlt_image_yuv422;
367                 uint8_t* image = 0;
368                 uint8_t* buffer = 0;
369                 if ( !mlt_frame_get_image( m_frame, &image, &format, &m_width, &m_height, 0 ) )
370                 {
371                         m_videoFrame = (IDeckLinkMutableVideoFrame*) mlt_deque_pop_back( m_videoFrameQ );
372                         m_videoFrame->GetBytes( (void**) &buffer );
373                         if ( m_displayMode->GetFieldDominance() == bmdUpperFieldFirst )
374                                 // convert lower field first to top field first
375                                 swab( image, buffer + m_width * 2, m_width * ( m_height - 1 ) * 2 );
376                         else
377                                 swab( image, buffer, m_width * m_height * 2 );
378                         m_deckLinkOutput->ScheduleVideoFrame( m_videoFrame, m_count * m_duration, m_duration, m_timescale );
379                         mlt_deque_push_front( m_videoFrameQ, m_videoFrame );
380                 }
381                 ++m_count;
382
383                 // Check for end of pre-roll
384                 if ( ++m_prerollCounter > m_preroll && m_isPrerolling )
385                 {
386                         // Start audio and video output
387                         m_deckLinkOutput->EndAudioPreroll();
388                         m_deckLinkOutput->StartScheduledPlayback( 0, m_timescale, 1.0 );
389                         m_isPrerolling = false;
390                 }
391
392                 return result;
393         }
394         
395         // *** DeckLink API implementation of IDeckLinkVideoOutputCallback IDeckLinkAudioOutputCallback *** //
396
397         // IUnknown needs only a dummy implementation
398         virtual HRESULT STDMETHODCALLTYPE QueryInterface( REFIID iid, LPVOID *ppv )
399                 { return E_NOINTERFACE; }
400         virtual ULONG STDMETHODCALLTYPE AddRef()
401                 { return 1; }
402         virtual ULONG STDMETHODCALLTYPE Release()
403                 { return 1; }
404         
405         /************************* DeckLink API Delegate Methods *****************************/
406         
407         virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted( IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult completed )
408         {
409                 // When a video frame has been released by the API, schedule another video frame to be output
410                 wakeup();
411                 
412                 return S_OK;
413         }
414
415         virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
416         {
417                 return mlt_consumer_is_stopped( &m_consumer ) ? S_FALSE : S_OK;
418         }
419         
420         virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples( bool preroll )
421         {
422                 // Provide more audio samples to the DeckLink API
423                 HRESULT result = S_OK;
424
425                 uint32_t count = m_fifo->used / m_channels;
426                 uint32_t buffered = count;
427
428                 if ( count
429                         // Stay under preferred buffer level
430                         && ( S_OK == m_deckLinkOutput->GetBufferedAudioSampleFrameCount( &buffered ) )
431                         && buffered < m_maxAudioBuffer )
432                 {
433                         uint32_t written = 0;
434                         
435                         buffered = m_maxAudioBuffer - buffered;
436                         count = buffered > count ? count : buffered;
437                         result = m_deckLinkOutput->ScheduleAudioSamples( m_fifo->buffer, count, 0, 0, &written );
438                         if ( written )
439                                 sample_fifo_remove( m_fifo, written * m_channels );
440                 }
441
442                 return result;
443         }
444 };
445
446 /** The main thread.
447  */
448
449 static void *run( void *arg )
450 {
451         // Map the argument to the object
452         DeckLinkConsumer* decklink = (DeckLinkConsumer*) arg;
453         mlt_consumer consumer = decklink->getConsumer();
454         
455         // Get the properties
456         mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
457
458         // Convenience functionality
459         int terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" );
460         int terminated = 0;
461
462         // Frame and size
463         mlt_frame frame = NULL;
464         
465         // Loop while running
466         while ( !terminated && mlt_properties_get_int( properties, "running" ) )
467         {
468                 // Get the frame
469                 frame = mlt_consumer_rt_frame( consumer );
470
471                 // Check for termination
472                 if ( terminate_on_pause && frame )
473                         terminated = mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0;
474
475                 // Check that we have a frame to work with
476                 if ( frame )
477                 {
478                         decklink->render( frame );
479                         if ( !decklink->isBuffering() )
480                                 decklink->wait();
481                         
482                         // Close the frame
483                         mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
484                 }
485         }
486
487         // Indicate that the consumer is stopped
488         decklink->stop();
489         mlt_properties_set_int( properties, "running", 0 );
490         mlt_consumer_stopped( consumer );
491
492         return NULL;
493 }
494
495 /** Start the consumer.
496  */
497
498 static int start( mlt_consumer consumer )
499 {
500         // Get the properties
501         mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
502         DeckLinkConsumer* decklink = (DeckLinkConsumer*) consumer->child;
503         int result = decklink->start( mlt_properties_get_int( properties, "preroll" ) ) ? 0 : 1;
504
505         // Check that we're not already running
506         if ( !result && !mlt_properties_get_int( properties, "running" ) )
507         {
508                 // Allocate a thread
509                 pthread_t *pthread = (pthread_t*) calloc( 1, sizeof( pthread_t ) );
510
511                 // Assign the thread to properties
512                 mlt_properties_set_data( properties, "pthread", pthread, sizeof( pthread_t ), free, NULL );
513
514                 // Set the running state
515                 mlt_properties_set_int( properties, "running", 1 );
516                 mlt_properties_set_int( properties, "joined", 0 );
517
518                 // Create the thread
519                 pthread_create( pthread, NULL, run, consumer->child );
520         }
521         return result;
522 }
523
524 /** Stop the consumer.
525  */
526
527 static int stop( mlt_consumer consumer )
528 {
529         // Get the properties
530         mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
531
532         // Check that we're running
533         if ( !mlt_properties_get_int( properties, "joined" ) )
534         {
535                 // Get the thread
536                 pthread_t *pthread = (pthread_t*) mlt_properties_get_data( properties, "pthread", NULL );
537                 
538                 if ( pthread )
539                 {
540                         // Stop the thread
541                         mlt_properties_set_int( properties, "running", 0 );
542                         mlt_properties_set_int( properties, "joined", 1 );
543         
544                         // Wait for termination
545                         pthread_join( *pthread, NULL );
546                 }
547         }
548
549         return 0;
550 }
551
552 /** Determine if the consumer is stopped.
553  */
554
555 static int is_stopped( mlt_consumer consumer )
556 {
557         // Get the properties
558         mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
559         return !mlt_properties_get_int( properties, "running" );
560 }
561
562 /** Close the consumer.
563  */
564
565 static void close( mlt_consumer consumer )
566 {
567         // Stop the consumer
568         mlt_consumer_stop( consumer );
569
570         // Close the parent
571         consumer->close = NULL;
572         mlt_consumer_close( consumer );
573
574         // Free the memory
575         delete (DeckLinkConsumer*) consumer->child;
576 }
577
578 extern "C" {
579
580 /** Initialise the consumer.
581  */
582
583 mlt_consumer consumer_decklink_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
584 {
585         // Allocate the consumer
586         DeckLinkConsumer* decklink = new DeckLinkConsumer();
587         mlt_consumer consumer = NULL;
588
589         // If allocated
590         if ( decklink && !mlt_consumer_init( decklink->getConsumer(), decklink, profile ) )
591         {
592                 // If initialises without error
593                 if ( decklink->open( arg? atoi(arg) : 0 ) )
594                 {
595                         consumer = decklink->getConsumer();
596                         
597                         // Setup callbacks
598                         consumer->close = close;
599                         consumer->start = start;
600                         consumer->stop = stop;
601                         consumer->is_stopped = is_stopped;
602                 }
603         }
604
605         // Return consumer
606         return consumer;
607 }
608
609
610 MLT_REPOSITORY
611 {
612         MLT_REGISTER( consumer_type, "decklink", consumer_decklink_init );
613 }
614
615 } // extern C