2 * consumer_decklink.c -- output through Blackmagic Design DeckLink
3 * Copyright (C) 2010 Dan Dennedy <dan@dennedy.org>
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.
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.
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
20 #define __STDC_FORMAT_MACROS /* see inttypes.h */
21 #include <framework/mlt.h>
31 #include "DeckLinkAPI_h.h"
33 #include "DeckLinkAPI.h"
36 static const unsigned PREROLL_MINIMUM = 3;
38 class DeckLinkConsumer
39 : public IDeckLinkVideoOutputCallback
42 mlt_consumer_s m_consumer;
43 IDeckLink* m_deckLink;
44 IDeckLinkOutput* m_deckLinkOutput;
45 IDeckLinkDisplayMode* m_displayMode;
48 BMDTimeValue m_duration;
49 BMDTimeScale m_timescale;
54 IDeckLinkMutableVideoFrame* m_decklinkFrame;
57 IDeckLinkKeyer* m_deckLinkKeyer;
58 bool m_terminate_on_pause;
62 pthread_t m_prerollThread;
64 IDeckLinkDisplayMode* getDisplayMode()
66 mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( getConsumer() ) );
67 IDeckLinkDisplayModeIterator* iter;
68 IDeckLinkDisplayMode* mode;
69 IDeckLinkDisplayMode* result = 0;
71 if ( m_deckLinkOutput->GetDisplayModeIterator( &iter ) == S_OK )
73 while ( !result && iter->Next( &mode ) == S_OK )
75 m_width = mode->GetWidth();
76 m_height = mode->GetHeight();
77 mode->GetFrameRate( &m_duration, &m_timescale );
78 m_fps = (double) m_timescale / m_duration;
79 int p = mode->GetFieldDominance() == bmdProgressiveFrame;
80 mlt_log_verbose( getConsumer(), "BMD mode %dx%d %.3f fps prog %d\n", m_width, m_height, m_fps, p );
82 if ( m_width == profile->width && p == profile->progressive
83 && m_fps == mlt_profile_fps( profile )
84 && ( m_height == profile->height || ( m_height == 486 && profile->height == 480 ) ) )
93 mlt_consumer getConsumer()
94 { return &m_consumer; }
98 if ( m_deckLinkKeyer )
99 m_deckLinkKeyer->Release();
100 if ( m_deckLinkOutput )
101 m_deckLinkOutput->Release();
103 m_deckLink->Release();
106 bool listDevices( mlt_properties properties )
108 IDeckLinkIterator* decklinkIterator = NULL;
113 HRESULT result = CoInitialize( NULL );
114 if ( FAILED( result ) )
115 throw "COM initialization failed";
116 result = CoCreateInstance( CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**) &decklinkIterator );
117 if ( FAILED( result ) )
118 throw "The DeckLink drivers are not installed.";
120 decklinkIterator = CreateDeckLinkIteratorInstance();
121 if ( !decklinkIterator )
122 throw "The DeckLink drivers are not installed.";
124 for ( ; decklinkIterator->Next( &m_deckLink ) == S_OK; i++ )
126 if ( m_deckLink->QueryInterface( IID_IDeckLinkOutput, (void**) &m_deckLinkOutput ) == S_OK )
129 if ( m_deckLink->GetModelName( (const char**) &name ) == S_OK )
131 const char *format = "device.%d";
132 char *key = (char*) calloc( 1, strlen( format ) + 1 );
134 sprintf( key, format, i );
135 mlt_properties_set( properties, key, name );
136 mlt_log_verbose( NULL, "[consumer decklink] %s = %s\n", key, name );
140 m_deckLinkOutput->Release();
142 m_deckLink->Release();
144 decklinkIterator->Release();
145 mlt_properties_set_int( properties, "devices", i );
146 mlt_log_verbose( NULL, "[consumer decklink] devices = %d\n", i );
150 catch ( const char *error )
152 if ( decklinkIterator )
153 decklinkIterator->Release();
154 mlt_log_error( getConsumer(), "%s\n", error );
159 bool open( unsigned card = 0 )
163 IDeckLinkIterator* deckLinkIterator = NULL;
164 HRESULT result = CoInitialize( NULL );
165 if ( FAILED( result ) )
167 mlt_log_error( getConsumer(), "COM initialization failed\n" );
170 result = CoCreateInstance( CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**) &deckLinkIterator );
171 if ( FAILED( result ) )
173 mlt_log_error( getConsumer(), "The DeckLink drivers not installed.\n" );
177 IDeckLinkIterator* deckLinkIterator = CreateDeckLinkIteratorInstance();
179 if ( !deckLinkIterator )
181 mlt_log_error( getConsumer(), "The DeckLink drivers not installed.\n" );
186 // Connect to the Nth DeckLink instance
188 if ( deckLinkIterator->Next( &m_deckLink ) != S_OK )
190 mlt_log_error( getConsumer(), "DeckLink card not found\n" );
191 deckLinkIterator->Release();
194 } while ( ++i <= card );
195 deckLinkIterator->Release();
197 // Obtain the audio/video output interface (IDeckLinkOutput)
198 if ( m_deckLink->QueryInterface( IID_IDeckLinkOutput, (void**)&m_deckLinkOutput ) != S_OK )
200 mlt_log_error( getConsumer(), "No DeckLink cards support output\n" );
201 m_deckLink->Release();
206 // Get the keyer interface
207 IDeckLinkAttributes *deckLinkAttributes = 0;
209 if ( m_deckLink->QueryInterface( IID_IDeckLinkAttributes, (void**) &deckLinkAttributes ) == S_OK )
216 if ( deckLinkAttributes->GetFlag( BMDDeckLinkSupportsInternalKeying, &flag ) == S_OK && flag )
218 if ( m_deckLink->QueryInterface( IID_IDeckLinkKeyer, (void**) &m_deckLinkKeyer ) != S_OK )
220 mlt_log_error( getConsumer(), "Failed to get keyer\n" );
221 m_deckLinkOutput->Release();
222 m_deckLinkOutput = 0;
223 m_deckLink->Release();
228 deckLinkAttributes->Release();
231 // Provide this class as a delegate to the audio and video output interfaces
232 m_deckLinkOutput->SetScheduledFrameCompletionCallback( this );
238 void* preroll_thread()
241 for ( unsigned i = 0; i < m_preroll; i++ )
242 ScheduleNextFrame( true );
244 // start scheduled playback
245 m_deckLinkOutput->StartScheduledPlayback( 0, m_timescale, 1.0 );
250 static void* preroll_thread_proxy( void* arg )
252 DeckLinkConsumer* self = static_cast< DeckLinkConsumer* >( arg );
253 return self->preroll_thread();
256 bool start( unsigned preroll )
258 mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
260 // Initialize members
263 m_decklinkFrame = NULL;
264 preroll = preroll < PREROLL_MINIMUM ? PREROLL_MINIMUM : preroll;
265 m_channels = mlt_properties_get_int( properties, "channels" );
266 m_isAudio = !mlt_properties_get_int( properties, "audio_off" );
267 m_terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" );
270 m_displayMode = getDisplayMode();
271 if ( !m_displayMode )
273 mlt_log_error( getConsumer(), "Profile is not compatible with decklink.\n" );
278 if ( m_deckLinkKeyer && ( m_isKeyer = mlt_properties_get_int( properties, "keyer" ) ) )
280 bool external = ( m_isKeyer == 2 );
281 double level = mlt_properties_get_double( properties, "keyer_level" );
283 if ( m_deckLinkKeyer->Enable( external ) != S_OK )
284 mlt_log_error( getConsumer(), "Failed to enable %s keyer\n",
285 external ? "external" : "internal" );
286 m_deckLinkKeyer->SetLevel( level <= 1 ? ( level > 0 ? 255 * level : 255 ) : 255 );
288 else if ( m_deckLinkKeyer )
290 m_deckLinkKeyer->Disable();
293 // Set the video output mode
294 if ( S_OK != m_deckLinkOutput->EnableVideoOutput( m_displayMode->GetDisplayMode(), bmdVideoOutputFlagDefault ) )
296 mlt_log_error( getConsumer(), "Failed to enable video output\n" );
300 // Set the audio output mode
303 m_deckLinkOutput->DisableAudioOutput();
306 if ( S_OK != m_deckLinkOutput->EnableAudioOutput( bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger,
307 m_channels, bmdAudioOutputStreamTimestamped ) )
309 mlt_log_error( getConsumer(), "Failed to enable audio output\n" );
317 // Do preroll in thread to ensure asynchronicity of mlt_consumer_start().
318 pthread_create( &m_prerollThread, NULL, preroll_thread_proxy, this );
320 // Set the running state
321 mlt_properties_set_int( properties, "running", 1 );
328 mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
329 bool wasRunning = !!mlt_properties_get_int( properties, "running" );
331 // set running state is 0
332 mlt_properties_set_int( properties, "running", 0 );
333 mlt_consumer_stopped( getConsumer() );
335 // Stop the audio and video output streams immediately
336 if ( m_deckLinkOutput )
338 m_deckLinkOutput->StopScheduledPlayback( 0, 0, 0 );
339 m_deckLinkOutput->DisableAudioOutput();
340 m_deckLinkOutput->DisableVideoOutput();
343 // release decklink frame
344 if ( m_decklinkFrame )
345 m_decklinkFrame->Release();
346 m_decklinkFrame = NULL;
349 pthread_join( m_prerollThread, NULL );
354 void renderAudio( mlt_frame frame )
356 mlt_audio_format format = mlt_audio_s16;
357 int frequency = bmdAudioSampleRate48kHz;
358 int samples = mlt_sample_calculator( m_fps, frequency, m_count );
361 if ( !mlt_frame_get_audio( frame, (void**) &pcm, &format, &frequency, &m_channels, &samples ) )
364 unsigned long written = 0;
366 uint32_t written = 0;
368 BMDTimeValue streamTime = m_count * frequency * m_duration / m_timescale;
369 m_deckLinkOutput->GetBufferedAudioSampleFrameCount( &written );
370 if ( written > (m_preroll + 1) * samples )
372 mlt_log_verbose( getConsumer(), "renderAudio: will flush %lu audiosamples\n", written );
373 m_deckLinkOutput->FlushBufferedAudioSamples();
376 m_deckLinkOutput->ScheduleAudioSamples( pcm, samples, streamTime, frequency, (unsigned long*) &written );
378 m_deckLinkOutput->ScheduleAudioSamples( pcm, samples, streamTime, frequency, &written );
381 if ( written != (uint32_t) samples )
382 mlt_log_verbose( getConsumer(), "renderAudio: samples=%d, written=%lu\n", samples, written );
386 bool createFrame( IDeckLinkMutableVideoFrame** decklinkFrame )
388 BMDPixelFormat format = m_isKeyer? bmdFormat8BitARGB : bmdFormat8BitYUV;
389 IDeckLinkMutableVideoFrame* frame = 0;
391 int stride = m_width * ( m_isKeyer? 4 : 2 );
393 *decklinkFrame = NULL;
395 // Generate a DeckLink video frame
396 if ( S_OK != m_deckLinkOutput->CreateVideoFrame( m_width, m_height,
397 stride, format, bmdFrameFlagDefault, &frame ) )
399 mlt_log_verbose( getConsumer(), "Failed to create video frame\n" );
404 // Make the first line black for field order correction.
405 if ( S_OK == frame->GetBytes( (void**) &buffer ) && buffer )
409 memset( buffer, 0, stride );
411 else for ( int i = 0; i < m_width; i++ )
418 *decklinkFrame = frame;
423 void renderVideo( mlt_frame frame )
425 mlt_image_format format = m_isKeyer? mlt_image_rgb24a : mlt_image_yuv422;
427 int rendered = mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "rendered");
428 int height = m_height;
430 if ( rendered && !mlt_frame_get_image( frame, &image, &format, &m_width, &height, 0 ) )
433 int stride = m_width * ( m_isKeyer? 4 : 2 );
435 if ( m_decklinkFrame )
436 m_decklinkFrame->Release();
437 if ( createFrame( &m_decklinkFrame ) )
438 m_decklinkFrame->GetBytes( (void**) &buffer );
442 int progressive = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "progressive" );
444 // NTSC SDI is always 486 lines
445 if ( m_height == 486 && height == 480 )
447 // blank first 6 lines
450 memset( buffer, 0, stride * 6 );
451 buffer += stride * 6;
453 else for ( int i = 0; i < m_width * 6; i++ )
461 // Normal non-keyer playout - needs byte swapping
462 if ( !progressive && m_displayMode->GetFieldDominance() == bmdUpperFieldFirst )
463 // convert lower field first to top field first
464 swab( (char*) image, (char*) buffer + stride, stride * ( height - 1 ) );
466 swab( (char*) image, (char*) buffer, stride * height );
468 else if ( !mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "test_image" ) )
470 // Normal keyer output
472 uint32_t* s = (uint32_t*) image;
473 uint32_t* d = (uint32_t*) buffer;
475 if ( !progressive && m_displayMode->GetFieldDominance() == bmdUpperFieldFirst )
477 // Correct field order
483 // Need to relocate alpha channel RGBA => ARGB
489 *d++ = ( *s << 8 ) | ( *s >> 24 );
496 // Keying blank frames - nullify alpha
497 memset( buffer, 0, stride * height );
501 if ( m_decklinkFrame )
502 m_deckLinkOutput->ScheduleVideoFrame( m_decklinkFrame, m_count * m_duration, m_duration, m_timescale );
505 mlt_log_verbose( getConsumer(), "dropped video frame %u\n", ++m_dropped );
508 HRESULT render( mlt_frame frame )
510 HRESULT result = S_OK;
513 double speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES(frame), "_speed" );
514 if ( m_isAudio && speed == 1.0 )
515 renderAudio( frame );
518 renderVideo( frame );
524 // *** DeckLink API implementation of IDeckLinkVideoOutputCallback IDeckLinkAudioOutputCallback *** //
526 // IUnknown needs only a dummy implementation
527 virtual HRESULT STDMETHODCALLTYPE QueryInterface( REFIID iid, LPVOID *ppv )
528 { return E_NOINTERFACE; }
529 virtual ULONG STDMETHODCALLTYPE AddRef()
531 virtual ULONG STDMETHODCALLTYPE Release()
534 /************************* DeckLink API Delegate Methods *****************************/
536 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted( IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult completed )
540 mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
542 if ( mlt_properties_get( properties, "priority" ) )
546 pthread_attr_t tattr;
547 struct sched_param param;
549 pthread_attr_init(&tattr);
550 pthread_attr_setschedpolicy(&tattr, SCHED_FIFO);
552 if ( !strcmp( "max", mlt_properties_get( properties, "priority" ) ) )
553 param.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1;
554 else if ( !strcmp( "min", mlt_properties_get( properties, "priority" ) ) )
555 param.sched_priority = sched_get_priority_min(SCHED_FIFO) + 1;
557 param.sched_priority = mlt_properties_get_int( properties, "priority" );
559 pthread_attr_setschedparam(&tattr, ¶m);
561 thread = pthread_self();
563 r = pthread_setschedparam(thread, SCHED_FIFO, ¶m);
565 mlt_log_verbose( getConsumer(),
566 "ScheduledFrameCompleted: pthread_setschedparam returned %d\n", r);
568 mlt_log_verbose( getConsumer(),
569 "ScheduledFrameCompleted: param.sched_priority=%d\n", param.sched_priority);
580 m_deckLinkOutput->GetBufferedAudioSampleFrameCount( &cnt );
583 mlt_log_debug( getConsumer(),
584 "ScheduledFrameCompleted: GetBufferedAudioSampleFrameCount %u -> %lu, m_count=%"PRIu64"\n",
585 m_acnt, cnt, m_count );
589 // When a video frame has been released by the API, schedule another video frame to be output
591 // ignore handler if frame was flushed
592 if ( bmdOutputFrameFlushed == completed )
595 // schedule next frame
596 ScheduleNextFrame( false );
598 // step forward frames counter if underrun
599 if ( bmdOutputFrameDisplayedLate == completed )
601 mlt_log_verbose( getConsumer(), "ScheduledFrameCompleted: bmdOutputFrameDisplayedLate == completed\n" );
604 if ( bmdOutputFrameDropped == completed )
606 mlt_log_verbose( getConsumer(), "ScheduledFrameCompleted: bmdOutputFrameDropped == completed\n" );
613 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
615 return mlt_consumer_is_stopped( getConsumer() ) ? S_FALSE : S_OK;
619 void ScheduleNextFrame( bool preroll )
622 mlt_consumer consumer = getConsumer();
624 // Get the properties
625 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
628 mlt_frame frame = NULL;
630 if( mlt_properties_get_int( properties, "running" ) || preroll )
632 frame = mlt_consumer_rt_frame( consumer );
637 mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
639 // terminate on pause
640 if ( m_terminate_on_pause &&
641 mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0 )
644 mlt_frame_close( frame );
650 /** Start the consumer.
653 static int start( mlt_consumer consumer )
655 // Get the properties
656 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
657 DeckLinkConsumer* decklink = (DeckLinkConsumer*) consumer->child;
658 return decklink->start( mlt_properties_get_int( properties, "preroll" ) ) ? 0 : 1;
661 /** Stop the consumer.
664 static int stop( mlt_consumer consumer )
666 // Get the properties
667 DeckLinkConsumer* decklink = (DeckLinkConsumer*) consumer->child;
668 return decklink->stop();
671 /** Determine if the consumer is stopped.
674 static int is_stopped( mlt_consumer consumer )
676 // Get the properties
677 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
678 return !mlt_properties_get_int( properties, "running" );
681 /** Close the consumer.
684 static void close( mlt_consumer consumer )
687 mlt_consumer_stop( consumer );
690 consumer->close = NULL;
691 mlt_consumer_close( consumer );
694 delete (DeckLinkConsumer*) consumer->child;
699 /** Initialise the consumer.
702 mlt_consumer consumer_decklink_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
704 // Allocate the consumer
705 DeckLinkConsumer* decklink = new DeckLinkConsumer();
706 mlt_consumer consumer = NULL;
709 if ( decklink && !mlt_consumer_init( decklink->getConsumer(), decklink, profile ) )
711 // If initialises without error
712 if ( decklink->listDevices( MLT_CONSUMER_PROPERTIES( decklink->getConsumer() ) ) &&
713 decklink->open( arg? atoi(arg) : 0 ) )
715 consumer = decklink->getConsumer();
718 consumer->close = close;
719 consumer->start = start;
720 consumer->stop = stop;
721 consumer->is_stopped = is_stopped;
722 mlt_properties_set( MLT_CONSUMER_PROPERTIES(consumer), "deinterlace_method", "onefield" );
730 extern mlt_producer producer_decklink_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
732 static mlt_properties metadata( mlt_service_type type, const char *id, void *data )
734 char file[ PATH_MAX ];
735 const char *service_type = NULL;
739 service_type = "consumer";
742 service_type = "producer";
747 snprintf( file, PATH_MAX, "%s/decklink/%s_%s.yml", mlt_environment( "MLT_DATA" ), service_type, id );
748 return mlt_properties_parse_yaml( file );
753 MLT_REGISTER( consumer_type, "decklink", consumer_decklink_init );
754 MLT_REGISTER( producer_type, "decklink", producer_decklink_init );
755 MLT_REGISTER_METADATA( consumer_type, "decklink", metadata, NULL );
756 MLT_REGISTER_METADATA( producer_type, "decklink", metadata, NULL );