]> git.sesse.net Git - mlt/blob - src/modules/decklink/consumer_decklink.cpp
fix decklink build for OS X
[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 #define __STDC_FORMAT_MACROS  /* see inttypes.h */
21 #include <framework/mlt.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <pthread.h>
25 #include <unistd.h>
26 #include <sys/time.h>
27 #include <limits.h>
28 #include <pthread.h>
29 #include "common.h"
30
31 static const unsigned PREROLL_MINIMUM = 3;
32
33 class DeckLinkConsumer
34         : public IDeckLinkVideoOutputCallback
35 {
36 private:
37         mlt_consumer_s              m_consumer;
38         IDeckLink*                  m_deckLink;
39         IDeckLinkOutput*            m_deckLinkOutput;
40         IDeckLinkDisplayMode*       m_displayMode;
41         int                         m_width;
42         int                         m_height;
43         BMDTimeValue                m_duration;
44         BMDTimeScale                m_timescale;
45         double                      m_fps;
46         uint64_t                    m_count;
47         int                         m_channels;
48         unsigned                    m_dropped;
49         IDeckLinkMutableVideoFrame* m_decklinkFrame;
50         bool                        m_isAudio;
51         int                         m_isKeyer;
52         IDeckLinkKeyer*             m_deckLinkKeyer;
53         bool                        m_terminate_on_pause;
54         uint32_t                    m_preroll;
55         uint32_t                    m_acnt;
56         bool                        m_reprio;
57         pthread_t                   m_prerollThread;
58
59         IDeckLinkDisplayMode* getDisplayMode()
60         {
61                 mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( getConsumer() ) );
62                 IDeckLinkDisplayModeIterator* iter = NULL;
63                 IDeckLinkDisplayMode* mode = NULL;
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 && p == profile->progressive
78                                          && m_fps == mlt_profile_fps( profile )
79                                          && ( m_height == profile->height || ( m_height == 486 && profile->height == 480 ) ) )
80                                         result = mode;
81                                 else
82                                         SAFE_RELEASE( mode );
83                         }
84                         SAFE_RELEASE( iter );
85                 }
86
87                 return result;
88         }
89
90 public:
91         mlt_consumer getConsumer()
92                 { return &m_consumer; }
93
94         DeckLinkConsumer()
95         {
96                 m_displayMode = NULL;
97                 m_deckLinkKeyer = NULL;
98                 m_deckLinkOutput = NULL;
99                 m_deckLink = NULL;
100                 m_decklinkFrame = NULL;
101         }
102
103         ~DeckLinkConsumer()
104         {
105                 SAFE_RELEASE( m_displayMode );
106                 SAFE_RELEASE( m_deckLinkKeyer );
107                 SAFE_RELEASE( m_deckLinkOutput );
108                 SAFE_RELEASE( m_deckLink );
109         }
110
111         bool open( unsigned card = 0 )
112         {
113                 unsigned i = 0;
114 #ifdef WIN32
115                 IDeckLinkIterator* deckLinkIterator = NULL;
116                 HRESULT result =  CoInitialize( NULL );
117                 if ( FAILED( result ) )
118                 {
119                         mlt_log_error( getConsumer(), "COM initialization failed\n" );
120                         return false;
121                 }
122                 result = CoCreateInstance( CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**) &deckLinkIterator );
123                 if ( FAILED( result ) )
124                 {
125                         mlt_log_error( getConsumer(), "The DeckLink drivers not installed.\n" );
126                         return false;
127                 }
128 #else
129                 IDeckLinkIterator* deckLinkIterator = CreateDeckLinkIteratorInstance();
130
131                 if ( !deckLinkIterator )
132                 {
133                         mlt_log_error( getConsumer(), "The DeckLink drivers not installed.\n" );
134                         return false;
135                 }
136 #endif
137
138                 // Connect to the Nth DeckLink instance
139                 for ( i = 0; deckLinkIterator->Next( &m_deckLink ) == S_OK ; i++)
140                 {
141                         if( i == card )
142                                 break;
143                         else
144                                 SAFE_RELEASE( m_deckLink );
145                 }
146                 SAFE_RELEASE( deckLinkIterator );
147                 if ( !m_deckLink )
148                 {
149                         mlt_log_error( getConsumer(), "DeckLink card not found\n" );
150                         return false;
151                 }
152
153                 // Obtain the audio/video output interface (IDeckLinkOutput)
154                 if ( m_deckLink->QueryInterface( IID_IDeckLinkOutput, (void**)&m_deckLinkOutput ) != S_OK )
155                 {
156                         mlt_log_error( getConsumer(), "No DeckLink cards support output\n" );
157                         SAFE_RELEASE( m_deckLink );
158                         return false;
159                 }
160
161                 // Get the keyer interface
162                 IDeckLinkAttributes *deckLinkAttributes = 0;
163                 if ( m_deckLink->QueryInterface( IID_IDeckLinkAttributes, (void**) &deckLinkAttributes ) == S_OK )
164                 {
165 #ifdef WIN32
166                         BOOL flag = FALSE;
167 #else
168                         bool flag = false;
169 #endif
170                         if ( deckLinkAttributes->GetFlag( BMDDeckLinkSupportsInternalKeying, &flag ) == S_OK && flag )
171                         {
172                                 if ( m_deckLink->QueryInterface( IID_IDeckLinkKeyer, (void**) &m_deckLinkKeyer ) != S_OK )
173                                 {
174                                         mlt_log_error( getConsumer(), "Failed to get keyer\n" );
175                                         SAFE_RELEASE( m_deckLinkOutput );
176                                         SAFE_RELEASE( m_deckLink );
177                                         return false;
178                                 }
179                         }
180                         SAFE_RELEASE( deckLinkAttributes );
181                 }
182
183                 // Provide this class as a delegate to the audio and video output interfaces
184                 m_deckLinkOutput->SetScheduledFrameCompletionCallback( this );
185
186                 return true;
187         }
188
189
190         void* preroll_thread()
191         {
192                 // preroll frames
193                 for ( unsigned i = 0; i < m_preroll; i++ )
194                         ScheduleNextFrame( true );
195
196                 // start scheduled playback
197                 m_deckLinkOutput->StartScheduledPlayback( 0, m_timescale, 1.0 );
198
199                 return 0;
200         }
201
202         static void* preroll_thread_proxy( void* arg )
203         {
204                 DeckLinkConsumer* self = static_cast< DeckLinkConsumer* >( arg );
205                 return self->preroll_thread();
206         }
207
208         bool start( unsigned preroll )
209         {
210                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
211
212                 // Initialize members
213                 m_count = 0;
214                 m_dropped = 0;
215                 m_decklinkFrame = NULL;
216                 preroll = preroll < PREROLL_MINIMUM ? PREROLL_MINIMUM : preroll;
217                 m_channels = mlt_properties_get_int( properties, "channels" );
218                 m_isAudio = !mlt_properties_get_int( properties, "audio_off" );
219                 m_terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" );
220
221
222                 m_displayMode = getDisplayMode();
223                 if ( !m_displayMode )
224                 {
225                         mlt_log_error( getConsumer(), "Profile is not compatible with decklink.\n" );
226                         return false;
227                 }
228
229                 // Set the keyer
230                 if ( m_deckLinkKeyer && ( m_isKeyer = mlt_properties_get_int( properties, "keyer" ) ) )
231                 {
232                         bool external = ( m_isKeyer == 2 );
233                         double level = mlt_properties_get_double( properties, "keyer_level" );
234
235                         if ( m_deckLinkKeyer->Enable( external ) != S_OK )
236                                 mlt_log_error( getConsumer(), "Failed to enable %s keyer\n",
237                                         external ? "external" : "internal" );
238                         m_deckLinkKeyer->SetLevel( level <= 1 ? ( level > 0 ? 255 * level : 255 ) : 255 );
239                 }
240                 else if ( m_deckLinkKeyer )
241                 {
242                         m_deckLinkKeyer->Disable();
243                 }
244
245                 // Set the video output mode
246                 if ( S_OK != m_deckLinkOutput->EnableVideoOutput( m_displayMode->GetDisplayMode(), bmdVideoOutputFlagDefault ) )
247                 {
248                         mlt_log_error( getConsumer(), "Failed to enable video output\n" );
249                         return false;
250                 }
251
252                 // Set the audio output mode
253                 if ( !m_isAudio )
254                 {
255                         m_deckLinkOutput->DisableAudioOutput();
256                         return true;
257                 }
258                 if ( S_OK != m_deckLinkOutput->EnableAudioOutput( bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger,
259                         m_channels, bmdAudioOutputStreamTimestamped ) )
260                 {
261                         mlt_log_error( getConsumer(), "Failed to enable audio output\n" );
262                         stop();
263                         return false;
264                 }
265
266                 m_preroll = preroll;
267                 m_reprio = false;
268
269                 // Do preroll in thread to ensure asynchronicity of mlt_consumer_start().
270                 pthread_create( &m_prerollThread, NULL, preroll_thread_proxy, this );
271
272                 // Set the running state
273                 mlt_properties_set_int( properties, "running", 1 );
274
275                 return true;
276         }
277
278         bool stop()
279         {
280                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
281                 bool wasRunning = !!mlt_properties_get_int( properties, "running" );
282
283                 // set running state is 0
284                 mlt_properties_set_int( properties, "running", 0 );
285                 mlt_consumer_stopped( getConsumer() );
286
287                 // Stop the audio and video output streams immediately
288                 if ( m_deckLinkOutput )
289                 {
290                         m_deckLinkOutput->StopScheduledPlayback( 0, 0, 0 );
291                         m_deckLinkOutput->DisableAudioOutput();
292                         m_deckLinkOutput->DisableVideoOutput();
293                 }
294
295                 // release decklink frame
296                 SAFE_RELEASE( m_decklinkFrame );
297
298                 if ( wasRunning )
299                         pthread_join( m_prerollThread, NULL );
300
301                 return true;
302         }
303
304         void renderAudio( mlt_frame frame )
305         {
306                 mlt_audio_format format = mlt_audio_s16;
307                 int frequency = bmdAudioSampleRate48kHz;
308                 int samples = mlt_sample_calculator( m_fps, frequency, m_count );
309                 int16_t *pcm = 0;
310
311                 if ( !mlt_frame_get_audio( frame, (void**) &pcm, &format, &frequency, &m_channels, &samples ) )
312                 {
313 #ifdef WIN32
314                         unsigned long written = 0;
315 #else
316                         uint32_t written = 0;
317 #endif
318                         BMDTimeValue streamTime = m_count * frequency * m_duration / m_timescale;
319                         m_deckLinkOutput->GetBufferedAudioSampleFrameCount( &written );
320                         if ( written > (m_preroll + 1) * samples )
321                         {
322                                 mlt_log_verbose( getConsumer(), "renderAudio: will flush %lu audiosamples\n", written );
323                                 m_deckLinkOutput->FlushBufferedAudioSamples();
324                         };
325 #ifdef WIN32
326                         m_deckLinkOutput->ScheduleAudioSamples( pcm, samples, streamTime, frequency, (unsigned long*) &written );
327 #else
328                         m_deckLinkOutput->ScheduleAudioSamples( pcm, samples, streamTime, frequency, &written );
329 #endif
330
331                         if ( written != (uint32_t) samples )
332                                 mlt_log_verbose( getConsumer(), "renderAudio: samples=%d, written=%lu\n", samples, written );
333                 }
334         }
335
336         bool createFrame( IDeckLinkMutableVideoFrame** decklinkFrame )
337         {
338                 BMDPixelFormat format = m_isKeyer? bmdFormat8BitARGB : bmdFormat8BitYUV;
339                 IDeckLinkMutableVideoFrame* frame = 0;
340                 uint8_t *buffer = 0;
341                 int stride = m_width * ( m_isKeyer? 4 : 2 );
342
343                 *decklinkFrame = NULL;
344
345                 // Generate a DeckLink video frame
346                 if ( S_OK != m_deckLinkOutput->CreateVideoFrame( m_width, m_height,
347                         stride, format, bmdFrameFlagDefault, &frame ) )
348                 {
349                         mlt_log_verbose( getConsumer(), "Failed to create video frame\n" );
350                         stop();
351                         return false;
352                 }
353
354                 // Make the first line black for field order correction.
355                 if ( S_OK == frame->GetBytes( (void**) &buffer ) && buffer )
356                 {
357                         if ( m_isKeyer )
358                         {
359                                 memset( buffer, 0, stride );
360                         }
361                         else for ( int i = 0; i < m_width; i++ )
362                         {
363                                 *buffer++ = 128;
364                                 *buffer++ = 16;
365                         }
366                 }
367
368                 *decklinkFrame = frame;
369
370                 return true;
371         }
372
373         void renderVideo( mlt_frame frame )
374         {
375                 mlt_image_format format = m_isKeyer? mlt_image_rgb24a : mlt_image_yuv422;
376                 uint8_t* image = 0;
377                 int rendered = mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "rendered");
378                 int height = m_height;
379
380                 if ( rendered && !mlt_frame_get_image( frame, &image, &format, &m_width, &height, 0 ) )
381                 {
382                         uint8_t* buffer = 0;
383                         int stride = m_width * ( m_isKeyer? 4 : 2 );
384
385                         SAFE_RELEASE( m_decklinkFrame );
386                         if ( createFrame( &m_decklinkFrame ) )
387                                 m_decklinkFrame->GetBytes( (void**) &buffer );
388
389                         if ( buffer )
390                         {
391                                 int progressive = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "progressive" );
392
393                                 // NTSC SDI is always 486 lines
394                                 if ( m_height == 486 && height == 480 )
395                                 {
396                                         // blank first 6 lines
397                                         if ( m_isKeyer )
398                                         {
399                                                 memset( buffer, 0, stride * 6 );
400                                                 buffer += stride * 6;
401                                         }
402                                         else for ( int i = 0; i < m_width * 6; i++ )
403                                         {
404                                                 *buffer++ = 128;
405                                                 *buffer++ = 16;
406                                         }
407                                 }
408                                 if ( !m_isKeyer )
409                                 {
410                                         // Normal non-keyer playout - needs byte swapping
411                                         if ( !progressive && m_displayMode->GetFieldDominance() == bmdUpperFieldFirst )
412                                                 // convert lower field first to top field first
413                                                 swab( (char*) image, (char*) buffer + stride, stride * ( height - 1 ) );
414                                         else
415                                                 swab( (char*) image, (char*) buffer, stride * height );
416                                 }
417                                 else if ( !mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "test_image" ) )
418                                 {
419                                         // Normal keyer output
420                                         int y = height + 1;
421                                         uint32_t* s = (uint32_t*) image;
422                                         uint32_t* d = (uint32_t*) buffer;
423
424                                         if ( !progressive && m_displayMode->GetFieldDominance() == bmdUpperFieldFirst )
425                                         {
426                                                 // Correct field order
427                                                 height--;
428                                                 y--;
429                                                 d += m_width;
430                                         }
431
432                                         // Need to relocate alpha channel RGBA => ARGB
433                                         while ( --y )
434                                         {
435                                                 int x = m_width + 1;
436                                                 while ( --x )
437                                                 {
438                                                         *d++ = ( *s << 8 ) | ( *s >> 24 );
439                                                         s++;
440                                                 }
441                                         }
442                                 }
443                                 else
444                                 {
445                                         // Keying blank frames - nullify alpha
446                                         memset( buffer, 0, stride * height );
447                                 }
448                         }
449                 }
450                 if ( m_decklinkFrame )
451                         m_deckLinkOutput->ScheduleVideoFrame( m_decklinkFrame, m_count * m_duration, m_duration, m_timescale );
452
453                 if ( !rendered )
454                         mlt_log_verbose( getConsumer(), "dropped video frame %u\n", ++m_dropped );
455         }
456
457         HRESULT render( mlt_frame frame )
458         {
459                 HRESULT result = S_OK;
460
461                 // Get the audio
462                 double speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES(frame), "_speed" );
463                 if ( m_isAudio && speed == 1.0 )
464                         renderAudio( frame );
465
466                 // Get the video
467                 renderVideo( frame );
468                 ++m_count;
469
470                 return result;
471         }
472
473         // *** DeckLink API implementation of IDeckLinkVideoOutputCallback IDeckLinkAudioOutputCallback *** //
474
475         // IUnknown needs only a dummy implementation
476         virtual HRESULT STDMETHODCALLTYPE QueryInterface( REFIID iid, LPVOID *ppv )
477                 { return E_NOINTERFACE; }
478         virtual ULONG STDMETHODCALLTYPE AddRef()
479                 { return 1; }
480         virtual ULONG STDMETHODCALLTYPE Release()
481                 { return 1; }
482
483         /************************* DeckLink API Delegate Methods *****************************/
484
485         virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted( IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult completed )
486         {
487                 if( !m_reprio )
488                 {
489                         mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
490
491                         if ( mlt_properties_get( properties, "priority" ) )
492                         {
493                                 int r;
494                                 pthread_t thread;
495                                 pthread_attr_t tattr;
496                                 struct sched_param param;
497
498                                 pthread_attr_init(&tattr);
499                                 pthread_attr_setschedpolicy(&tattr, SCHED_FIFO);
500
501                                 if ( !strcmp( "max", mlt_properties_get( properties, "priority" ) ) )
502                                         param.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1;
503                                 else if ( !strcmp( "min", mlt_properties_get( properties, "priority" ) ) )
504                                         param.sched_priority = sched_get_priority_min(SCHED_FIFO) + 1;
505                                 else
506                                         param.sched_priority = mlt_properties_get_int( properties, "priority" );
507
508                                 pthread_attr_setschedparam(&tattr, &param);
509
510                                 thread = pthread_self();
511
512                                 r = pthread_setschedparam(thread, SCHED_FIFO, &param);
513                                 if( r )
514                                         mlt_log_verbose( getConsumer(),
515                                                 "ScheduledFrameCompleted: pthread_setschedparam returned %d\n", r);
516                                 else
517                                         mlt_log_verbose( getConsumer(),
518                                                 "ScheduledFrameCompleted: param.sched_priority=%d\n", param.sched_priority);
519                         };
520
521                         m_reprio = true;
522                 };
523
524 #ifdef WIN32
525                 unsigned long cnt;
526 #else
527                 uint32_t cnt;
528 #endif
529                 m_deckLinkOutput->GetBufferedAudioSampleFrameCount( &cnt );
530                 if ( cnt != m_acnt )
531                 {
532                         mlt_log_debug( getConsumer(),
533                                 "ScheduledFrameCompleted: GetBufferedAudioSampleFrameCount %u -> %lu, m_count=%"PRIu64"\n",
534                                 m_acnt, cnt, m_count );
535                         m_acnt = cnt;
536                 }
537
538                 // When a video frame has been released by the API, schedule another video frame to be output
539
540                 // ignore handler if frame was flushed
541                 if ( bmdOutputFrameFlushed == completed )
542                         return S_OK;
543
544                 // schedule next frame
545                 ScheduleNextFrame( false );
546
547                 // step forward frames counter if underrun
548                 if ( bmdOutputFrameDisplayedLate == completed )
549                 {
550                         mlt_log_verbose( getConsumer(), "ScheduledFrameCompleted: bmdOutputFrameDisplayedLate == completed\n" );
551                         m_count++;
552                 }
553                 if ( bmdOutputFrameDropped == completed )
554                 {
555                         mlt_log_verbose( getConsumer(), "ScheduledFrameCompleted: bmdOutputFrameDropped == completed\n" );
556                         m_count++;
557                 }
558
559                 return S_OK;
560         }
561
562         virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
563         {
564                 return mlt_consumer_is_stopped( getConsumer() ) ? S_FALSE : S_OK;
565         }
566
567
568         void ScheduleNextFrame( bool preroll )
569         {
570                 // get the consumer
571                 mlt_consumer consumer = getConsumer();
572
573                 // Get the properties
574                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
575
576                 // Frame and size
577                 mlt_frame frame = NULL;
578
579                 if( mlt_properties_get_int( properties, "running" ) || preroll )
580                 {
581                         frame = mlt_consumer_rt_frame( consumer );
582                         if ( frame != NULL )
583                         {
584                                 render( frame );
585
586                                 mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
587
588                                 // terminate on pause
589                                 if ( m_terminate_on_pause &&
590                                         mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0 )
591                                         stop();
592
593                                 mlt_frame_close( frame );
594                         }
595                 }
596         }
597 };
598
599 /** Start the consumer.
600  */
601
602 static int start( mlt_consumer consumer )
603 {
604         // Get the properties
605         mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
606         DeckLinkConsumer* decklink = (DeckLinkConsumer*) consumer->child;
607         return decklink->start( mlt_properties_get_int( properties, "preroll" ) ) ? 0 : 1;
608 }
609
610 /** Stop the consumer.
611  */
612
613 static int stop( mlt_consumer consumer )
614 {
615         // Get the properties
616         DeckLinkConsumer* decklink = (DeckLinkConsumer*) consumer->child;
617         return decklink->stop();
618 }
619
620 /** Determine if the consumer is stopped.
621  */
622
623 static int is_stopped( mlt_consumer consumer )
624 {
625         // Get the properties
626         mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
627         return !mlt_properties_get_int( properties, "running" );
628 }
629
630 /** Close the consumer.
631  */
632
633 static void close( mlt_consumer consumer )
634 {
635         // Stop the consumer
636         mlt_consumer_stop( consumer );
637
638         // Close the parent
639         consumer->close = NULL;
640         mlt_consumer_close( consumer );
641
642         // Free the memory
643         delete (DeckLinkConsumer*) consumer->child;
644 }
645
646 extern "C" {
647
648 // Listen for the list_devices property to be set
649 static void on_property_changed( void*, mlt_properties properties, const char *name )
650 {
651         IDeckLinkIterator* decklinkIterator = NULL;
652         IDeckLink* decklink = NULL;
653         IDeckLinkInput* decklinkOutput = NULL;
654         int i = 0;
655
656         if ( name && !strcmp( name, "list_devices" ) )
657                 mlt_event_block( (mlt_event) mlt_properties_get_data( properties, "list-devices-event", NULL ) );
658         else
659                 return;
660
661 #ifdef WIN32
662         if ( FAILED( CoInitialize( NULL ) ) )
663                 return;
664         if ( FAILED( CoCreateInstance( CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**) &decklinkIterator ) ) )
665                 return;
666 #else
667         if ( !( decklinkIterator = CreateDeckLinkIteratorInstance() ) )
668                 return;
669 #endif
670         for ( ; decklinkIterator->Next( &decklink ) == S_OK; i++ )
671         {
672                 if ( decklink->QueryInterface( IID_IDeckLinkOutput, (void**) &decklinkOutput ) == S_OK )
673                 {
674                         DLString name = NULL;
675                         if ( decklink->GetModelName( &name ) == S_OK )
676                         {
677                                 char *name_cstr = getCString( name );
678                                 const char *format = "device.%d";
679                                 char *key = (char*) calloc( 1, strlen( format ) + 1 );
680
681                                 sprintf( key, format, i );
682                                 mlt_properties_set( properties, key, name_cstr );
683                                 free( key );
684                                 freeDLString( name );
685                                 freeCString( name_cstr );
686                         }
687                         SAFE_RELEASE( decklinkOutput );
688                 }
689                 SAFE_RELEASE( decklink );
690         }
691         SAFE_RELEASE( decklinkIterator );
692         mlt_properties_set_int( properties, "devices", i );
693 }
694
695 /** Initialise the consumer.
696  */
697
698 mlt_consumer consumer_decklink_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
699 {
700         // Allocate the consumer
701         DeckLinkConsumer* decklink = new DeckLinkConsumer();
702         mlt_consumer consumer = NULL;
703
704         // If allocated
705         if ( decklink && !mlt_consumer_init( decklink->getConsumer(), decklink, profile ) )
706         {
707                 // If initialises without error
708                 if ( decklink->open( arg? atoi(arg) : 0 ) )
709                 {
710                         consumer = decklink->getConsumer();
711                         mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
712
713                         // Setup callbacks
714                         consumer->close = close;
715                         consumer->start = start;
716                         consumer->stop = stop;
717                         consumer->is_stopped = is_stopped;
718                         mlt_properties_set( properties, "deinterlace_method", "onefield" );
719
720                         mlt_event event = mlt_events_listen( properties, properties, "property-changed", (mlt_listener) on_property_changed );
721                         mlt_properties_set_data( properties, "list-devices-event", event, 0, NULL, NULL );
722                 }
723         }
724
725         // Return consumer
726         return consumer;
727 }
728
729 extern mlt_producer producer_decklink_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
730
731 static mlt_properties metadata( mlt_service_type type, const char *id, void *data )
732 {
733         char file[ PATH_MAX ];
734         const char *service_type = NULL;
735         switch ( type )
736         {
737                 case consumer_type:
738                         service_type = "consumer";
739                         break;
740                 case producer_type:
741                         service_type = "producer";
742                         break;
743                 default:
744                         return NULL;
745         }
746         snprintf( file, PATH_MAX, "%s/decklink/%s_%s.yml", mlt_environment( "MLT_DATA" ), service_type, id );
747         return mlt_properties_parse_yaml( file );
748 }
749
750 MLT_REPOSITORY
751 {
752         MLT_REGISTER( consumer_type, "decklink", consumer_decklink_init );
753         MLT_REGISTER( producer_type, "decklink", producer_decklink_init );
754         MLT_REGISTER_METADATA( consumer_type, "decklink", metadata, NULL );
755         MLT_REGISTER_METADATA( producer_type, "decklink", metadata, NULL );
756 }
757
758 } // extern C