]> git.sesse.net Git - mlt/blob - src/modules/decklink/consumer_decklink.cpp
Prevent decklink audio buffer overflow.
[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 <limits.h>
27 #ifdef WIN32
28 #include <objbase.h>
29 #include "DeckLinkAPI_h.h"
30 #else
31 #include "DeckLinkAPI.h"
32 #endif
33
34 static const unsigned PREROLL_MINIMUM = 3;
35
36 class DeckLinkConsumer
37         : public IDeckLinkVideoOutputCallback
38 {
39 private:
40         mlt_consumer_s              m_consumer;
41         IDeckLink*                  m_deckLink;
42         IDeckLinkOutput*            m_deckLinkOutput;
43         IDeckLinkDisplayMode*       m_displayMode;
44         int                         m_width;
45         int                         m_height;
46         BMDTimeValue                m_duration;
47         BMDTimeScale                m_timescale;
48         double                      m_fps;
49         uint64_t                    m_count;
50         int                         m_channels;
51         unsigned                    m_dropped;
52         IDeckLinkMutableVideoFrame* m_decklinkFrame;
53         bool                        m_isAudio;
54         int                         m_isKeyer;
55         IDeckLinkKeyer*             m_deckLinkKeyer;
56         bool                        m_terminate_on_pause;
57         uint32_t                    m_preroll;
58
59         IDeckLinkDisplayMode* getDisplayMode()
60         {
61                 mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( getConsumer() ) );
62                 IDeckLinkDisplayModeIterator* iter;
63                 IDeckLinkDisplayMode* mode;
64                 IDeckLinkDisplayMode* result = 0;
65                 
66                 if ( m_deckLinkOutput->GetDisplayModeIterator( &iter ) == S_OK )
67                 {
68                         while ( !result && iter->Next( &mode ) == S_OK )
69                         {
70                                 m_width = mode->GetWidth();
71                                 m_height = mode->GetHeight();
72                                 mode->GetFrameRate( &m_duration, &m_timescale );
73                                 m_fps = (double) m_timescale / m_duration;
74                                 int p = mode->GetFieldDominance() == bmdProgressiveFrame;
75                                 mlt_log_verbose( getConsumer(), "BMD mode %dx%d %.3f fps prog %d\n", m_width, m_height, m_fps, p );
76                                 
77                                 if ( m_width == profile->width && m_height == profile->height && p == profile->progressive
78                                          && m_fps == mlt_profile_fps( profile ) )
79                                         result = mode;
80                         }
81                 }
82                 
83                 return result;
84         }
85         
86 public:
87         mlt_consumer getConsumer()
88                 { return &m_consumer; }
89         
90         ~DeckLinkConsumer()
91         {
92                 if ( m_deckLinkKeyer )
93                         m_deckLinkKeyer->Release();
94                 if ( m_deckLinkOutput )
95                         m_deckLinkOutput->Release();
96                 if ( m_deckLink )
97                         m_deckLink->Release();
98         }
99         
100         bool open( unsigned card = 0 )
101         {
102                 unsigned i = 0;
103 #ifdef WIN32
104                 IDeckLinkIterator* deckLinkIterator = NULL;
105                 HRESULT result =  CoInitialize( NULL );
106                 if ( FAILED( result ) )
107                 {
108                         mlt_log_error( getConsumer(), "COM initialization failed\n" );
109                         return false;
110                 }
111                 result = CoCreateInstance( CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**) &deckLinkIterator );
112                 if ( FAILED( result ) )
113                 {
114                         mlt_log_error( getConsumer(), "The DeckLink drivers not installed.\n" );
115                         return false;
116                 }
117 #else
118                 IDeckLinkIterator* deckLinkIterator = CreateDeckLinkIteratorInstance();
119                 
120                 if ( !deckLinkIterator )
121                 {
122                         mlt_log_error( getConsumer(), "The DeckLink drivers not installed.\n" );
123                         return false;
124                 }
125 #endif
126                 
127                 // Connect to the Nth DeckLink instance
128                 do {
129                         if ( deckLinkIterator->Next( &m_deckLink ) != S_OK )
130                         {
131                                 mlt_log_error( getConsumer(), "DeckLink card not found\n" );
132                                 deckLinkIterator->Release();
133                                 return false;
134                         }
135                 } while ( ++i <= card );
136                 deckLinkIterator->Release();
137                 
138                 // Obtain the audio/video output interface (IDeckLinkOutput)
139                 if ( m_deckLink->QueryInterface( IID_IDeckLinkOutput, (void**)&m_deckLinkOutput ) != S_OK )
140                 {
141                         mlt_log_error( getConsumer(), "No DeckLink cards support output\n" );
142                         m_deckLink->Release();
143                         m_deckLink = 0;
144                         return false;
145                 }
146                 
147                 // Get the keyer interface
148                 IDeckLinkAttributes *deckLinkAttributes = 0;
149                 m_deckLinkKeyer = 0;
150                 if ( m_deckLink->QueryInterface( IID_IDeckLinkAttributes, (void**) &deckLinkAttributes ) == S_OK )
151                 {
152 #ifdef WIN32
153                         BOOL flag = FALSE;
154 #else
155                         bool flag = false;
156 #endif
157                         if ( deckLinkAttributes->GetFlag( BMDDeckLinkSupportsInternalKeying, &flag ) == S_OK && flag )
158                         {
159                                 if ( m_deckLink->QueryInterface( IID_IDeckLinkKeyer, (void**) &m_deckLinkKeyer ) != S_OK )
160                                 {
161                                         mlt_log_error( getConsumer(), "Failed to get keyer\n" );
162                                         m_deckLinkOutput->Release();
163                                         m_deckLinkOutput = 0;
164                                         m_deckLink->Release();
165                                         m_deckLink = 0;
166                                         return false;
167                                 }
168                         }
169                         deckLinkAttributes->Release();
170                 }
171
172                 // Provide this class as a delegate to the audio and video output interfaces
173                 m_deckLinkOutput->SetScheduledFrameCompletionCallback( this );
174                 
175                 return true;
176         }
177         
178         bool start( unsigned preroll )
179         {
180                 unsigned i;
181                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
182
183                 // Initialize members
184                 m_count = 0;
185                 m_dropped = 0;
186                 m_decklinkFrame = NULL;
187                 preroll = preroll < PREROLL_MINIMUM ? PREROLL_MINIMUM : preroll;
188                 m_channels = mlt_properties_get_int( properties, "channels" );
189                 m_isAudio = !mlt_properties_get_int( properties, "audio_off" );
190                 m_terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" );
191
192
193                 m_displayMode = getDisplayMode();
194                 if ( !m_displayMode )
195                 {
196                         mlt_log_error( getConsumer(), "Profile is not compatible with decklink.\n" );
197                         return false;
198                 }
199                 
200                 // Set the keyer
201                 if ( m_deckLinkKeyer && ( m_isKeyer = mlt_properties_get_int( properties, "keyer" ) ) )
202                 {
203                         bool external = (m_isKeyer == 2);
204                         double level = mlt_properties_get_double( properties, "keyer_level" );
205
206                         if ( m_deckLinkKeyer->Enable( external ) != S_OK )
207                                 mlt_log_error( getConsumer(), "Failed to enable %s keyer\n",
208                                         external ? "external" : "internal" );
209                         m_deckLinkKeyer->SetLevel( level <= 1 ? ( level > 0 ? 255 * level : 255 ) : 255 );
210                 }
211                 else if ( m_deckLinkKeyer )
212                 {
213                         m_deckLinkKeyer->Disable();
214                 }
215
216                 // Set the video output mode
217                 if ( S_OK != m_deckLinkOutput->EnableVideoOutput( m_displayMode->GetDisplayMode(), bmdVideoOutputFlagDefault) )
218                 {
219                         mlt_log_error( getConsumer(), "Failed to enable video output\n" );
220                         return false;
221                 }
222
223                 // Set the audio output mode
224                 if ( !m_isAudio )
225                 {
226                         m_deckLinkOutput->DisableAudioOutput();
227                         return true;
228                 }
229                 if ( S_OK != m_deckLinkOutput->EnableAudioOutput( bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger,
230                         m_channels, bmdAudioOutputStreamTimestamped ) )
231                 {
232                         mlt_log_error( getConsumer(), "Failed to enable audio output\n" );
233                         stop();
234                         return false;
235                 }
236
237                 m_preroll = preroll;
238
239                 // preroll frames
240                 for( i = 0; i < preroll; i++ )
241                         ScheduleNextFrame( true );
242
243                 // start scheduled playback
244                 m_deckLinkOutput->StartScheduledPlayback( 0, m_timescale, 1.0 );
245
246                 // Set the running state
247                 mlt_properties_set_int( properties, "running", 1 );
248
249                 return true;
250         }
251         
252         bool stop()
253         {
254                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
255
256                 // set running state is 0
257                 mlt_properties_set_int( properties, "running", 0 );
258                 mlt_consumer_stopped( getConsumer() );
259
260                 // release decklink frame
261                 if ( m_decklinkFrame )
262                         m_decklinkFrame->Release();
263                 m_decklinkFrame = NULL;
264
265                 // Stop the audio and video output streams immediately
266                 if ( m_deckLinkOutput )
267                 {
268                         m_deckLinkOutput->StopScheduledPlayback( 0, 0, 0 );
269                         m_deckLinkOutput->DisableAudioOutput();
270                         m_deckLinkOutput->DisableVideoOutput();
271                 }
272
273                 return true;
274         }
275
276         void renderAudio( mlt_frame frame )
277         {
278                 mlt_audio_format format = mlt_audio_s16;
279                 int frequency = bmdAudioSampleRate48kHz;
280                 int samples = mlt_sample_calculator( m_fps, frequency, m_count );
281                 int16_t *pcm = 0;
282
283                 if ( !mlt_frame_get_audio( frame, (void**) &pcm, &format, &frequency, &m_channels, &samples ) )
284                 {
285                         uint32_t written = 0;
286                         BMDTimeValue streamTime = m_count * frequency * m_duration / m_timescale;
287                         m_deckLinkOutput->GetBufferedAudioSampleFrameCount(&written);
288                         if ( written > (m_preroll + 1) * samples )
289                         {
290                                 mlt_log_verbose( getConsumer(), "renderAudio: will flush %d audiosamples\n", written);
291                                 m_deckLinkOutput->FlushBufferedAudioSamples();
292                         };
293 #ifdef WIN32
294                         m_deckLinkOutput->ScheduleAudioSamples( pcm, samples, streamTime, frequency, (unsigned long*) &written );
295 #else
296                         m_deckLinkOutput->ScheduleAudioSamples( pcm, samples, streamTime, frequency, &written );
297 #endif
298
299                         if ( written != (uint32_t) samples )
300                                 mlt_log_verbose( getConsumer(), "renderAudio: samples=%d, written=%u\n", samples, written );
301                 }
302         }
303
304         bool createFrame( IDeckLinkMutableVideoFrame** decklinkFrame )
305         {
306                 BMDPixelFormat format = m_isKeyer? bmdFormat8BitARGB : bmdFormat8BitYUV;
307                 IDeckLinkMutableVideoFrame* frame = 0;
308                 uint8_t *buffer = 0;
309                 int stride = m_width * ( m_isKeyer? 4 : 2 );
310
311                 *decklinkFrame = NULL;
312
313                 // Generate a DeckLink video frame
314                 if ( S_OK != m_deckLinkOutput->CreateVideoFrame( m_width, m_height,
315                         stride, format, bmdFrameFlagDefault, &frame ) )
316                 {
317                         mlt_log_verbose( getConsumer(), "Failed to create video frame\n" );
318                         stop();
319                         return false;
320                 }
321                 
322                 // Make the first line black for field order correction.
323                 if ( S_OK == frame->GetBytes( (void**) &buffer ) && buffer )
324                 {
325                         if ( m_isKeyer )
326                         {
327                                 memset( buffer, 0, stride );
328                         }
329                         else for ( int i = 0; i < m_width; i++ )
330                         {
331                                 *buffer++ = 128;
332                                 *buffer++ = 16;
333                         }
334                 }
335
336                 *decklinkFrame = frame;
337
338                 return true;
339         }
340
341         void renderVideo( mlt_frame frame )
342         {
343                 mlt_image_format format = m_isKeyer? mlt_image_rgb24a : mlt_image_yuv422;
344                 uint8_t* image = 0;
345                 int rendered = mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "rendered");
346
347                 if ( rendered && !mlt_frame_get_image( frame, &image, &format, &m_width, &m_height, 0 ) )
348                 {
349                         uint8_t* buffer = 0;
350                         int stride = m_width * ( m_isKeyer? 4 : 2 );
351
352                         if ( m_decklinkFrame )
353                                 m_decklinkFrame->Release();
354                         if ( createFrame( &m_decklinkFrame ) )
355                                 m_decklinkFrame->GetBytes( (void**) &buffer );
356
357                         if ( buffer )
358                         {
359                                 int progressive = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "progressive" );
360
361                                 if ( !m_isKeyer )
362                                 {
363                                         // Normal non-keyer playout - needs byte swapping
364                                         if ( !progressive && m_displayMode->GetFieldDominance() == bmdUpperFieldFirst )
365                                                 // convert lower field first to top field first
366                                                 swab( (char*) image, (char*) buffer + stride, stride * ( m_height - 1 ) );
367                                         else
368                                                 swab( (char*) image, (char*) buffer, stride * m_height );
369                                 }
370                                 else if ( !mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "test_image" ) )
371                                 {
372                                         // Normal keyer output
373                                         int y = m_height + 1;
374                                         uint32_t* s = (uint32_t*) image;
375                                         uint32_t* d = (uint32_t*) buffer;
376
377                                         if ( !progressive && m_displayMode->GetFieldDominance() == bmdUpperFieldFirst )
378                                         {
379                                                 // Correct field order
380                                                 m_height--;
381                                                 y--;
382                                                 d += m_width;
383                                         }
384
385                                         // Need to relocate alpha channel RGBA => ARGB
386                                         while ( --y )
387                                         {
388                                                 int x = m_width + 1;
389                                                 while ( --x )
390                                                 {
391                                                         *d++ = ( *s << 8 ) | ( *s >> 24 );
392                                                         s++;
393                                                 }
394                                         }
395                                 }
396                                 else
397                                 {
398                                         // Keying blank frames - nullify alpha
399                                         memset( buffer, 0, stride * m_height );
400                                 }
401                         }
402                 }
403                 if ( m_decklinkFrame )
404                         m_deckLinkOutput->ScheduleVideoFrame( m_decklinkFrame, m_count * m_duration, m_duration, m_timescale );
405
406                 if ( !rendered )
407                         mlt_log_verbose( getConsumer(), "dropped video frame %u\n", ++m_dropped );
408         }
409
410         HRESULT render( mlt_frame frame )
411         {
412                 HRESULT result = S_OK;
413
414                 // Get the audio
415                 double speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES(frame), "_speed" );
416                 if ( m_isAudio && speed == 1.0 )
417                         renderAudio( frame );
418
419                 // Get the video
420                 renderVideo( frame );
421                 ++m_count;
422
423                 return result;
424         }
425         
426         // *** DeckLink API implementation of IDeckLinkVideoOutputCallback IDeckLinkAudioOutputCallback *** //
427
428         // IUnknown needs only a dummy implementation
429         virtual HRESULT STDMETHODCALLTYPE QueryInterface( REFIID iid, LPVOID *ppv )
430                 { return E_NOINTERFACE; }
431         virtual ULONG STDMETHODCALLTYPE AddRef()
432                 { return 1; }
433         virtual ULONG STDMETHODCALLTYPE Release()
434                 { return 1; }
435         
436         /************************* DeckLink API Delegate Methods *****************************/
437         
438         virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted( IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult completed )
439         {
440                 // When a video frame has been released by the API, schedule another video frame to be output
441
442                 // ignore handler if frame was flushed
443                 if(bmdOutputFrameFlushed == completed)
444                         return S_OK;
445
446                 // schedule next frame
447                 ScheduleNextFrame(false);
448
449                 // step forward frames counter if underrun
450                 if(bmdOutputFrameDisplayedLate == completed)
451                 {
452                         mlt_log_verbose( getConsumer(), "ScheduledFrameCompleted: bmdOutputFrameDisplayedLate == completed\n");
453                         m_count++;
454                 }
455                 if(bmdOutputFrameDropped == completed)
456                 {
457                         mlt_log_verbose( getConsumer(), "ScheduledFrameCompleted: bmdOutputFrameDropped == completed\n");
458                         m_count++;
459                 }
460
461                 return S_OK;
462         }
463
464         virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
465         {
466                 return mlt_consumer_is_stopped( getConsumer() ) ? S_FALSE : S_OK;
467         }
468         
469
470         void ScheduleNextFrame(bool preroll)
471         {
472                 // get the consumer
473                 mlt_consumer consumer = getConsumer();
474
475                 // Get the properties
476                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
477
478                 // Frame and size
479                 mlt_frame frame = NULL;
480
481                 if( mlt_properties_get_int( properties, "running" ) || preroll )
482                 {
483                         frame = mlt_consumer_rt_frame( consumer );
484                         if ( frame != NULL )
485                         {
486                                 render( frame );
487
488                                 mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
489
490                                 // terminate on pause
491                                 if (m_terminate_on_pause &&
492                                         mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0)
493                                         stop();
494
495                                 mlt_frame_close( frame );
496                         }
497                 }
498         }
499 };
500
501 /** Start the consumer.
502  */
503
504 static int start( mlt_consumer consumer )
505 {
506         // Get the properties
507         mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
508         DeckLinkConsumer* decklink = (DeckLinkConsumer*) consumer->child;
509         return decklink->start( mlt_properties_get_int( properties, "preroll" ) ) ? 0 : 1;
510 }
511
512 /** Stop the consumer.
513  */
514
515 static int stop( mlt_consumer consumer )
516 {
517         // Get the properties
518         DeckLinkConsumer* decklink = (DeckLinkConsumer*) consumer->child;
519         return decklink->stop();
520 }
521
522 /** Determine if the consumer is stopped.
523  */
524
525 static int is_stopped( mlt_consumer consumer )
526 {
527         // Get the properties
528         mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
529         return !mlt_properties_get_int( properties, "running" );
530 }
531
532 /** Close the consumer.
533  */
534
535 static void close( mlt_consumer consumer )
536 {
537         // Stop the consumer
538         mlt_consumer_stop( consumer );
539
540         // Close the parent
541         consumer->close = NULL;
542         mlt_consumer_close( consumer );
543
544         // Free the memory
545         delete (DeckLinkConsumer*) consumer->child;
546 }
547
548 extern "C" {
549
550 /** Initialise the consumer.
551  */
552
553 mlt_consumer consumer_decklink_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
554 {
555         // Allocate the consumer
556         DeckLinkConsumer* decklink = new DeckLinkConsumer();
557         mlt_consumer consumer = NULL;
558
559         // If allocated
560         if ( decklink && !mlt_consumer_init( decklink->getConsumer(), decklink, profile ) )
561         {
562                 // If initialises without error
563                 if ( decklink->open( arg? atoi(arg) : 0 ) )
564                 {
565                         consumer = decklink->getConsumer();
566                         
567                         // Setup callbacks
568                         consumer->close = close;
569                         consumer->start = start;
570                         consumer->stop = stop;
571                         consumer->is_stopped = is_stopped;
572                         mlt_properties_set( MLT_CONSUMER_PROPERTIES(consumer), "deinterlace_method", "onefield" );
573                 }
574         }
575
576         // Return consumer
577         return consumer;
578 }
579
580 extern mlt_producer producer_decklink_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
581
582 static mlt_properties metadata( mlt_service_type type, const char *id, void *data )
583 {
584         char file[ PATH_MAX ];
585         const char *service_type = NULL;
586         switch ( type )
587         {
588                 case consumer_type:
589                         service_type = "consumer";
590                         break;
591                 case producer_type:
592                         service_type = "producer";
593                         break;
594                 default:
595                         return NULL;
596         }
597         snprintf( file, PATH_MAX, "%s/decklink/%s_%s.yml", mlt_environment( "MLT_DATA" ), service_type, id );
598         return mlt_properties_parse_yaml( file );
599 }
600
601 MLT_REPOSITORY
602 {
603         MLT_REGISTER( consumer_type, "decklink", consumer_decklink_init );
604         MLT_REGISTER( producer_type, "decklink", producer_decklink_init );
605         MLT_REGISTER_METADATA( consumer_type, "decklink", metadata, NULL );
606         MLT_REGISTER_METADATA( producer_type, "decklink", metadata, NULL );
607 }
608
609 } // extern C