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