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