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 #include <framework/mlt.h>
27 #include "DeckLinkAPI.h"
29 static const unsigned PREROLL_MINIMUM = 3;
31 class DeckLinkConsumer
32 : public IDeckLinkVideoOutputCallback
35 mlt_consumer_s m_consumer;
36 IDeckLink* m_deckLink;
37 IDeckLinkOutput* m_deckLinkOutput;
38 IDeckLinkDisplayMode* m_displayMode;
41 BMDTimeValue m_duration;
42 BMDTimeScale m_timescale;
47 IDeckLinkMutableVideoFrame* m_decklinkFrame;
50 IDeckLinkKeyer* m_deckLinkKeyer;
51 bool m_terminate_on_pause;
53 IDeckLinkDisplayMode* getDisplayMode()
55 mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( getConsumer() ) );
56 IDeckLinkDisplayModeIterator* iter;
57 IDeckLinkDisplayMode* mode;
58 IDeckLinkDisplayMode* result = 0;
60 if ( m_deckLinkOutput->GetDisplayModeIterator( &iter ) == S_OK )
62 while ( !result && iter->Next( &mode ) == S_OK )
64 m_width = mode->GetWidth();
65 m_height = mode->GetHeight();
66 mode->GetFrameRate( &m_duration, &m_timescale );
67 m_fps = (double) m_timescale / m_duration;
68 int p = mode->GetFieldDominance() == bmdProgressiveFrame;
69 mlt_log_verbose( getConsumer(), "BMD mode %dx%d %.3f fps prog %d\n", m_width, m_height, m_fps, p );
71 if ( m_width == profile->width && m_height == profile->height && p == profile->progressive
72 && m_fps == mlt_profile_fps( profile ) )
81 mlt_consumer getConsumer()
82 { return &m_consumer; }
86 if ( m_deckLinkKeyer )
87 m_deckLinkKeyer->Release();
88 if ( m_deckLinkOutput )
89 m_deckLinkOutput->Release();
91 m_deckLink->Release();
94 bool open( unsigned card = 0 )
96 IDeckLinkIterator* deckLinkIterator = CreateDeckLinkIteratorInstance();
99 if ( !deckLinkIterator )
101 mlt_log_error( getConsumer(), "The DeckLink drivers not installed.\n" );
105 // Connect to the Nth DeckLink instance
107 if ( deckLinkIterator->Next( &m_deckLink ) != S_OK )
109 mlt_log_error( getConsumer(), "DeckLink card not found\n" );
110 deckLinkIterator->Release();
113 } while ( ++i <= card );
114 deckLinkIterator->Release();
116 // Obtain the audio/video output interface (IDeckLinkOutput)
117 if ( m_deckLink->QueryInterface( IID_IDeckLinkOutput, (void**)&m_deckLinkOutput ) != S_OK )
119 mlt_log_error( getConsumer(), "No DeckLink cards support output\n" );
120 m_deckLink->Release();
125 // Get the keyer interface
126 IDeckLinkAttributes *deckLinkAttributes = 0;
128 if ( m_deckLink->QueryInterface( IID_IDeckLinkAttributes, (void**) &deckLinkAttributes ) == S_OK )
131 if ( deckLinkAttributes->GetFlag( BMDDeckLinkSupportsInternalKeying, &flag ) == S_OK && flag )
133 if ( m_deckLink->QueryInterface( IID_IDeckLinkKeyer, (void**) &m_deckLinkKeyer ) != S_OK )
135 mlt_log_error( getConsumer(), "Failed to get keyer\n" );
136 m_deckLinkOutput->Release();
137 m_deckLinkOutput = 0;
138 m_deckLink->Release();
143 deckLinkAttributes->Release();
146 // Provide this class as a delegate to the audio and video output interfaces
147 m_deckLinkOutput->SetScheduledFrameCompletionCallback( this );
152 bool start( unsigned preroll )
155 mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
157 // Initialize members
160 m_decklinkFrame = NULL;
161 preroll = preroll < PREROLL_MINIMUM ? PREROLL_MINIMUM : preroll;
162 m_channels = mlt_properties_get_int( properties, "channels" );
163 m_isAudio = !mlt_properties_get_int( properties, "audio_off" );
164 m_terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" );
167 m_displayMode = getDisplayMode();
168 if ( !m_displayMode )
170 mlt_log_error( getConsumer(), "Profile is not compatible with decklink.\n" );
175 if ( m_deckLinkKeyer && ( m_isKeyer = mlt_properties_get_int( properties, "keyer" ) ) )
177 bool external = (m_isKeyer == 2);
178 double level = mlt_properties_get_double( properties, "keyer_level" );
180 if ( m_deckLinkKeyer->Enable( external ) != S_OK )
181 mlt_log_error( getConsumer(), "Failed to enable %s keyer\n",
182 external ? "external" : "internal" );
183 m_deckLinkKeyer->SetLevel( level <= 1 ? ( level > 0 ? 255 * level : 255 ) : 255 );
185 else if ( m_deckLinkKeyer )
187 m_deckLinkKeyer->Disable();
190 // Set the video output mode
191 if ( S_OK != m_deckLinkOutput->EnableVideoOutput( m_displayMode->GetDisplayMode(), bmdVideoOutputFlagDefault) )
193 mlt_log_error( getConsumer(), "Failed to enable video output\n" );
197 // Set the audio output mode
200 m_deckLinkOutput->DisableAudioOutput();
203 if ( S_OK != m_deckLinkOutput->EnableAudioOutput( bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger,
204 m_channels, bmdAudioOutputStreamTimestamped ) )
206 mlt_log_error( getConsumer(), "Failed to enable audio output\n" );
212 for( i = 0; i < preroll; i++ )
213 ScheduleNextFrame( true );
215 // start scheduled playback
216 m_deckLinkOutput->StartScheduledPlayback( 0, m_timescale, 1.0 );
218 // Set the running state
219 mlt_properties_set_int( properties, "running", 1 );
226 mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
228 // set running state is 0
229 mlt_properties_set_int( properties, "running", 0 );
230 mlt_consumer_stopped( getConsumer() );
232 // release decklink frame
233 if ( m_decklinkFrame )
234 m_decklinkFrame->Release();
235 m_decklinkFrame = NULL;
237 // Stop the audio and video output streams immediately
238 if ( m_deckLinkOutput )
240 m_deckLinkOutput->StopScheduledPlayback( 0, 0, 0 );
241 m_deckLinkOutput->DisableAudioOutput();
242 m_deckLinkOutput->DisableVideoOutput();
248 void renderAudio( mlt_frame frame )
250 mlt_audio_format format = mlt_audio_s16;
251 int frequency = bmdAudioSampleRate48kHz;
252 int samples = mlt_sample_calculator( m_fps, frequency, m_count );
255 if ( !mlt_frame_get_audio( frame, (void**) &pcm, &format, &frequency, &m_channels, &samples ) )
257 uint32_t written = 0;
259 m_deckLinkOutput->ScheduleAudioSamples( pcm, samples, m_count * frequency / m_fps, frequency, &written );
261 if ( written != samples )
262 mlt_log_verbose( getConsumer(), "renderAudio: samples=%d, written=%d\n", samples, written );
266 bool createFrame( IDeckLinkMutableVideoFrame** decklinkFrame )
268 BMDPixelFormat format = m_isKeyer? bmdFormat8BitARGB : bmdFormat8BitYUV;
269 IDeckLinkMutableVideoFrame* frame = 0;
271 int stride = m_width * ( m_isKeyer? 4 : 2 );
273 *decklinkFrame = NULL;
275 // Generate a DeckLink video frame
276 if ( S_OK != m_deckLinkOutput->CreateVideoFrame( m_width, m_height,
277 stride, format, bmdFrameFlagDefault, &frame ) )
279 mlt_log_verbose( getConsumer(), "Failed to create video frame\n" );
284 // Make the first line black for field order correction.
285 if ( S_OK == frame->GetBytes( (void**) &buffer ) && buffer )
289 memset( buffer, 0, stride );
291 else for ( int i = 0; i < m_width; i++ )
298 *decklinkFrame = frame;
303 void renderVideo( mlt_frame frame )
305 mlt_image_format format = m_isKeyer? mlt_image_rgb24a : mlt_image_yuv422;
307 int rendered = mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "rendered");
309 if ( rendered && !mlt_frame_get_image( frame, &image, &format, &m_width, &m_height, 0 ) )
312 int stride = m_width * ( m_isKeyer? 4 : 2 );
314 if ( m_decklinkFrame )
315 m_decklinkFrame->Release();
316 if ( createFrame( &m_decklinkFrame ) )
317 m_decklinkFrame->GetBytes( (void**) &buffer );
321 int progressive = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "progressive" );
325 // Normal non-keyer playout - needs byte swapping
326 if ( !progressive && m_displayMode->GetFieldDominance() == bmdUpperFieldFirst )
327 // convert lower field first to top field first
328 swab( image, buffer + stride, stride * ( m_height - 1 ) );
330 swab( image, buffer, stride * m_height );
332 else if ( !mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "test_image" ) )
334 // Normal keyer output
335 int y = m_height + 1;
336 uint32_t* s = (uint32_t*) image;
337 uint32_t* d = (uint32_t*) buffer;
339 if ( !progressive && m_displayMode->GetFieldDominance() == bmdUpperFieldFirst )
341 // Correct field order
347 // Need to relocate alpha channel RGBA => ARGB
353 *d++ = ( *s << 8 ) | ( *s >> 24 );
360 // Keying blank frames - nullify alpha
361 memset( buffer, 0, stride * m_height );
365 if ( m_decklinkFrame )
366 m_deckLinkOutput->ScheduleVideoFrame( m_decklinkFrame, m_count * m_duration, m_duration, m_timescale );
369 mlt_log_verbose( getConsumer(), "dropped video frame %u\n", ++m_dropped );
372 HRESULT render( mlt_frame frame )
374 HRESULT result = S_OK;
377 double speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES(frame), "_speed" );
378 if ( m_isAudio && speed == 1.0 )
379 renderAudio( frame );
382 renderVideo( frame );
388 // *** DeckLink API implementation of IDeckLinkVideoOutputCallback IDeckLinkAudioOutputCallback *** //
390 // IUnknown needs only a dummy implementation
391 virtual HRESULT STDMETHODCALLTYPE QueryInterface( REFIID iid, LPVOID *ppv )
392 { return E_NOINTERFACE; }
393 virtual ULONG STDMETHODCALLTYPE AddRef()
395 virtual ULONG STDMETHODCALLTYPE Release()
398 /************************* DeckLink API Delegate Methods *****************************/
400 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted( IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult completed )
402 // When a video frame has been released by the API, schedule another video frame to be output
404 // ignore handler if frame was flushed
405 if(bmdOutputFrameFlushed == completed)
408 // schedule next frame
409 ScheduleNextFrame(false);
411 // step forward frames counter if underrun
412 if(bmdOutputFrameDisplayedLate == completed)
414 mlt_log_verbose( getConsumer(), "ScheduledFrameCompleted: bmdOutputFrameDisplayedLate == completed\n");
421 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
423 return mlt_consumer_is_stopped( getConsumer() ) ? S_FALSE : S_OK;
427 void ScheduleNextFrame(bool preroll)
430 mlt_consumer consumer = getConsumer();
432 // Get the properties
433 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
436 mlt_frame frame = NULL;
438 if( mlt_properties_get_int( properties, "running" ) || preroll )
440 frame = mlt_consumer_rt_frame( consumer );
445 mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
447 // terminate on pause
448 if (m_terminate_on_pause &&
449 mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0)
452 mlt_frame_close( frame );
458 /** Start the consumer.
461 static int start( mlt_consumer consumer )
463 // Get the properties
464 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
465 DeckLinkConsumer* decklink = (DeckLinkConsumer*) consumer->child;
466 return decklink->start( mlt_properties_get_int( properties, "preroll" ) ) ? 0 : 1;
469 /** Stop the consumer.
472 static int stop( mlt_consumer consumer )
474 // Get the properties
475 DeckLinkConsumer* decklink = (DeckLinkConsumer*) consumer->child;
476 return decklink->stop();
479 /** Determine if the consumer is stopped.
482 static int is_stopped( mlt_consumer consumer )
484 // Get the properties
485 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
486 return !mlt_properties_get_int( properties, "running" );
489 /** Close the consumer.
492 static void close( mlt_consumer consumer )
495 mlt_consumer_stop( consumer );
498 consumer->close = NULL;
499 mlt_consumer_close( consumer );
502 delete (DeckLinkConsumer*) consumer->child;
507 /** Initialise the consumer.
510 mlt_consumer consumer_decklink_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
512 // Allocate the consumer
513 DeckLinkConsumer* decklink = new DeckLinkConsumer();
514 mlt_consumer consumer = NULL;
517 if ( decklink && !mlt_consumer_init( decklink->getConsumer(), decklink, profile ) )
519 // If initialises without error
520 if ( decklink->open( arg? atoi(arg) : 0 ) )
522 consumer = decklink->getConsumer();
525 consumer->close = close;
526 consumer->start = start;
527 consumer->stop = stop;
528 consumer->is_stopped = is_stopped;
529 mlt_properties_set( MLT_CONSUMER_PROPERTIES(consumer), "deinterlace_method", "onefield" );
537 extern mlt_producer producer_decklink_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
539 static mlt_properties metadata( mlt_service_type type, const char *id, void *data )
541 char file[ PATH_MAX ];
542 const char *service_type = NULL;
546 service_type = "consumer";
549 service_type = "producer";
554 snprintf( file, PATH_MAX, "%s/decklink/%s_%s.yml", mlt_environment( "MLT_DATA" ), service_type, id );
555 return mlt_properties_parse_yaml( file );
560 MLT_REGISTER( consumer_type, "decklink", consumer_decklink_init );
561 MLT_REGISTER( producer_type, "decklink", producer_decklink_init );
562 MLT_REGISTER_METADATA( consumer_type, "decklink", metadata, NULL );
563 MLT_REGISTER_METADATA( producer_type, "decklink", metadata, NULL );