]> git.sesse.net Git - mlt/blob - src/modules/decklink/producer_decklink.cpp
Fix compiler warnings due to non-virtual destructors.
[mlt] / src / modules / decklink / producer_decklink.cpp
1 /*
2  * producer_decklink.c -- input from Blackmagic Design DeckLink
3  * Copyright (C) 2011 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 <pthread.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <limits.h>
26 #include <sys/time.h>
27 #include "common.h"
28
29 class DeckLinkProducer
30         : public IDeckLinkInputCallback
31 {
32 private:
33         mlt_producer     m_producer;
34         IDeckLink*       m_decklink;
35         IDeckLinkInput*  m_decklinkInput;
36         mlt_deque        m_queue;
37         pthread_mutex_t  m_mutex;
38         pthread_cond_t   m_condition;
39         bool             m_started;
40         int              m_dropped;
41         bool             m_isBuffering;
42         int              m_topFieldFirst;
43         int              m_colorspace;
44         int              m_vancLines;
45         mlt_cache        m_cache;
46         bool             m_reprio;
47
48         BMDDisplayMode getDisplayMode( mlt_profile profile, int vancLines )
49         {
50                 IDeckLinkDisplayModeIterator* iter = NULL;
51                 IDeckLinkDisplayMode* mode = NULL;
52                 BMDDisplayMode result = (BMDDisplayMode) bmdDisplayModeNotSupported;
53
54                 if ( m_decklinkInput->GetDisplayModeIterator( &iter ) == S_OK )
55                 {
56                         while ( !result && iter->Next( &mode ) == S_OK )
57                         {
58                                 int width = mode->GetWidth();
59                                 int height = mode->GetHeight();
60                                 BMDTimeValue duration;
61                                 BMDTimeScale timescale;
62                                 mode->GetFrameRate( &duration, &timescale );
63                                 double fps = (double) timescale / duration;
64                                 int p = mode->GetFieldDominance() == bmdProgressiveFrame;
65                                 m_topFieldFirst = mode->GetFieldDominance() == bmdUpperFieldFirst;
66                                 m_colorspace = ( mode->GetFlags() & bmdDisplayModeColorspaceRec709 ) ? 709 : 601;
67                                 mlt_log_verbose( getProducer(), "BMD mode %dx%d %.3f fps prog %d tff %d\n", width, height, fps, p, m_topFieldFirst );
68
69                                 if ( width == profile->width && p == profile->progressive
70                                          && ( height + vancLines == profile->height || ( height == 486 && profile->height == 480 + vancLines ) )
71                                          && (int) fps == (int) mlt_profile_fps( profile ) )
72                                         result = mode->GetDisplayMode();
73                                 SAFE_RELEASE( mode );
74                         }
75                         SAFE_RELEASE( iter );
76                 }
77
78                 return result;
79         }
80
81 public:
82
83         void setProducer( mlt_producer producer )
84                 { m_producer = producer; }
85
86         mlt_producer getProducer() const
87                 { return m_producer; }
88
89         DeckLinkProducer()
90         {
91                 m_producer = NULL;
92                 m_decklink = NULL;
93                 m_decklinkInput = NULL;
94         }
95
96         virtual ~DeckLinkProducer()
97         {
98                 if ( m_queue )
99                 {
100                         stop();
101                         mlt_deque_close( m_queue );
102                         pthread_mutex_destroy( &m_mutex );
103                         pthread_cond_destroy( &m_condition );
104                         mlt_cache_close( m_cache );
105                 }
106                 SAFE_RELEASE( m_decklinkInput );
107                 SAFE_RELEASE( m_decklink );
108         }
109
110         bool open( unsigned card =  0 )
111         {
112                 IDeckLinkIterator* decklinkIterator = NULL;
113                 try
114                 {
115 #ifdef WIN32
116                         HRESULT result =  CoInitialize( NULL );
117                         if ( FAILED( result ) )
118                                 throw "COM initialization failed";
119                         result = CoCreateInstance( CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**) &decklinkIterator );
120                         if ( FAILED( result ) )
121                                 throw "The DeckLink drivers are not installed.";
122 #else
123                         decklinkIterator = CreateDeckLinkIteratorInstance();
124                         if ( !decklinkIterator )
125                                 throw "The DeckLink drivers are not installed.";
126 #endif
127                         // Connect to the Nth DeckLink instance
128                         for ( unsigned i = 0; decklinkIterator->Next( &m_decklink ) == S_OK ; i++)
129                         {
130                                 if ( i == card )
131                                         break;
132                                 else
133                                         SAFE_RELEASE( m_decklink );
134                         }
135                         SAFE_RELEASE( decklinkIterator );
136                         if ( !m_decklink )
137                                 throw "DeckLink card not found.";
138
139                         // Get the input interface
140                         if ( m_decklink->QueryInterface( IID_IDeckLinkInput, (void**) &m_decklinkInput ) != S_OK )
141                                 throw "No DeckLink cards support input.";
142
143                         // Provide this class as a delegate to the input callback
144                         m_decklinkInput->SetCallback( this );
145
146                         // Initialize other members
147                         pthread_mutex_init( &m_mutex, NULL );
148                         pthread_cond_init( &m_condition, NULL );
149                         m_queue = mlt_deque_init();
150                         m_started = false;
151                         m_dropped = 0;
152                         m_isBuffering = true;
153                         m_cache = mlt_cache_init();
154
155                         // 3 covers YADIF and increasing framerate use cases
156                         mlt_cache_set_size( m_cache, 3 );
157                 }
158                 catch ( const char *error )
159                 {
160                         SAFE_RELEASE( m_decklinkInput );
161                         SAFE_RELEASE( m_decklink );
162                         mlt_log_error( getProducer(), "%s\n", error );
163                         return false;
164                 }
165                 return true;
166         }
167
168         bool start( mlt_profile profile = 0 )
169         {
170                 if ( m_started )
171                         return false;
172                 try
173                 {
174                         // Initialize some members
175                         m_vancLines = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "vanc" );
176                         if ( m_vancLines == -1 )
177                                 m_vancLines = profile->height <= 512 ? 26 : 32;
178
179                         if ( !profile )
180                                 profile = mlt_service_profile( MLT_PRODUCER_SERVICE( getProducer() ) );
181
182                         // Get the display mode
183                         BMDDisplayMode displayMode = getDisplayMode( profile, m_vancLines );
184                         if ( displayMode == (BMDDisplayMode) bmdDisplayModeNotSupported )
185                         {
186                                 mlt_log_info( getProducer(), "profile = %dx%d %f fps %s\n", profile->width, profile->height,
187                                                           mlt_profile_fps( profile ), profile->progressive? "progressive" : "interlace" );
188                                 throw "Profile is not compatible with decklink.";
189                         }
190
191                         // Determine if supports input format detection
192 #ifdef WIN32
193                         BOOL doesDetectFormat = FALSE;
194 #else
195                         bool doesDetectFormat = false;
196 #endif
197                         IDeckLinkAttributes *decklinkAttributes = 0;
198                         if ( m_decklink->QueryInterface( IID_IDeckLinkAttributes, (void**) &decklinkAttributes ) == S_OK )
199                         {
200                                 if ( decklinkAttributes->GetFlag( BMDDeckLinkSupportsInputFormatDetection, &doesDetectFormat ) != S_OK )
201                                         doesDetectFormat = false;
202                                 SAFE_RELEASE( decklinkAttributes );
203                         }
204                         mlt_log_verbose( getProducer(), "%s format detection\n", doesDetectFormat ? "supports" : "does not support" );
205
206                         // Enable video capture
207                         BMDPixelFormat pixelFormat = bmdFormat8BitYUV;
208                         BMDVideoInputFlags flags = doesDetectFormat ? bmdVideoInputEnableFormatDetection : bmdVideoInputFlagDefault;
209                         if ( S_OK != m_decklinkInput->EnableVideoInput( displayMode, pixelFormat, flags ) )
210                                 throw "Failed to enable video capture.";
211
212                         // Enable audio capture
213                         BMDAudioSampleRate sampleRate = bmdAudioSampleRate48kHz;
214                         BMDAudioSampleType sampleType = bmdAudioSampleType16bitInteger;
215                         int channels = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "channels" );
216                         if ( S_OK != m_decklinkInput->EnableAudioInput( sampleRate, sampleType, channels ) )
217                                 throw "Failed to enable audio capture.";
218
219                         // Start capture
220                         m_dropped = 0;
221                         mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "dropped", m_dropped );
222                         m_started = m_decklinkInput->StartStreams() == S_OK;
223                         if ( !m_started )
224                                 throw "Failed to start capture.";
225                 }
226                 catch ( const char *error )
227                 {
228                         m_decklinkInput->DisableVideoInput();
229                         mlt_log_error( getProducer(), "%s\n", error );
230                         return false;
231                 }
232                 return true;
233         }
234
235         void stop()
236         {
237                 if ( !m_started )
238                         return;
239                 m_started = false;
240
241                 // Release the wait in getFrame
242                 pthread_mutex_lock( &m_mutex );
243                 pthread_cond_broadcast( &m_condition );
244                 pthread_mutex_unlock( &m_mutex );
245
246                 m_decklinkInput->StopStreams();
247                 m_decklinkInput->DisableVideoInput();
248                 m_decklinkInput->DisableAudioInput();
249
250                 // Cleanup queue
251                 pthread_mutex_lock( &m_mutex );
252                 while ( mlt_frame frame = (mlt_frame) mlt_deque_pop_back( m_queue ) )
253                         mlt_frame_close( frame );
254                 pthread_mutex_unlock( &m_mutex );
255         }
256
257         mlt_frame getFrame()
258         {
259                 struct timeval now;
260                 struct timespec tm;
261                 double fps = mlt_producer_get_fps( getProducer() );
262                 mlt_position position = mlt_producer_position( getProducer() );
263                 mlt_frame frame = mlt_cache_get_frame( m_cache, position );
264
265                 // Allow the buffer to fill to the requested initial buffer level.
266                 if ( m_isBuffering )
267                 {
268                         int prefill = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "prefill" );
269                         int buffer = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "buffer" );
270
271                         m_isBuffering = false;
272                         prefill = prefill > buffer ? buffer : prefill;
273                         pthread_mutex_lock( &m_mutex );
274                         while ( mlt_deque_count( m_queue ) < prefill )
275                         {
276                                 // Wait up to buffer/fps seconds
277                                 gettimeofday( &now, NULL );
278                                 long usec = now.tv_sec * 1000000 + now.tv_usec;
279                                 usec += 1000000 * buffer / fps;
280                                 tm.tv_sec = usec / 1000000;
281                                 tm.tv_nsec = (usec % 1000000) * 1000;
282                                 if ( pthread_cond_timedwait( &m_condition, &m_mutex, &tm ) )
283                                         break;
284                         }
285                         pthread_mutex_unlock( &m_mutex );
286                 }
287
288                 if ( !frame )
289                 {
290                         // Wait if queue is empty
291                         pthread_mutex_lock( &m_mutex );
292                         while ( mlt_deque_count( m_queue ) < 1 )
293                         {
294                                 // Wait up to twice frame duration
295                                 gettimeofday( &now, NULL );
296                                 long usec = now.tv_sec * 1000000 + now.tv_usec;
297                                 usec += 2000000 / fps;
298                                 tm.tv_sec = usec / 1000000;
299                                 tm.tv_nsec = (usec % 1000000) * 1000;
300                                 if ( pthread_cond_timedwait( &m_condition, &m_mutex, &tm ) )
301                                         // Stop waiting if error (timed out)
302                                         break;
303                         }
304                         frame = ( mlt_frame ) mlt_deque_pop_front( m_queue );
305                         pthread_mutex_unlock( &m_mutex );
306
307                         // add to cache
308                         if ( frame )
309                         {
310                                 mlt_frame_set_position( frame, position );
311                                 mlt_cache_put_frame( m_cache, frame );
312                         }
313                 }
314
315                 // Set frame timestamp and properties
316                 if ( frame )
317                 {
318                         mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( getProducer() ) );
319                         mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
320                         mlt_properties_set_int( properties, "progressive", profile->progressive );
321                         mlt_properties_set_int( properties, "meta.media.progressive", profile->progressive );
322                         mlt_properties_set_int( properties, "top_field_first", m_topFieldFirst );
323                         mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) );
324                         mlt_properties_set_int( properties, "meta.media.sample_aspect_num", profile->sample_aspect_num );
325                         mlt_properties_set_int( properties, "meta.media.sample_aspect_den", profile->sample_aspect_den );
326                         mlt_properties_set_int( properties, "meta.media.frame_rate_num", profile->frame_rate_num );
327                         mlt_properties_set_int( properties, "meta.media.frame_rate_den", profile->frame_rate_den );
328                         mlt_properties_set_int( properties, "width", profile->width );
329                         mlt_properties_set_int( properties, "meta.media.width", profile->width );
330                         mlt_properties_set_int( properties, "height", profile->height );
331                         mlt_properties_set_int( properties, "meta.media.height", profile->height );
332                         mlt_properties_set_int( properties, "format", mlt_image_yuv422 );
333                         mlt_properties_set_int( properties, "colorspace", m_colorspace );
334                         mlt_properties_set_int( properties, "meta.media.colorspace", m_colorspace );
335                         mlt_properties_set_int( properties, "audio_frequency", 48000 );
336                         mlt_properties_set_int( properties, "audio_channels",
337                                 mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "channels" ) );
338                 }
339                 else
340                         mlt_log_warning( getProducer(), "buffer underrun\n" );
341
342                 return frame;
343         }
344
345         // *** DeckLink API implementation of IDeckLinkInputCallback *** //
346
347         // IUnknown needs only a dummy implementation
348         virtual HRESULT STDMETHODCALLTYPE QueryInterface( REFIID iid, LPVOID *ppv )
349                 { return E_NOINTERFACE; }
350         virtual ULONG STDMETHODCALLTYPE AddRef()
351                 { return 1; }
352         virtual ULONG STDMETHODCALLTYPE Release()
353                 { return 1; }
354
355         /************************* DeckLink API Delegate Methods *****************************/
356
357         virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(
358                         IDeckLinkVideoInputFrame* video,
359                         IDeckLinkAudioInputPacket* audio )
360         {
361                 if( !m_reprio )
362                 {
363                         mlt_properties properties = MLT_PRODUCER_PROPERTIES( getProducer() );
364
365                         if ( mlt_properties_get( properties, "priority" ) )
366                         {
367                                 int r;
368                                 pthread_t thread;
369                                 pthread_attr_t tattr;
370                                 struct sched_param param;
371
372                                 pthread_attr_init(&tattr);
373                                 pthread_attr_setschedpolicy(&tattr, SCHED_FIFO);
374
375                                 if ( !strcmp( "max", mlt_properties_get( properties, "priority" ) ) )
376                                         param.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1;
377                                 else if ( !strcmp( "min", mlt_properties_get( properties, "priority" ) ) )
378                                         param.sched_priority = sched_get_priority_min(SCHED_FIFO) + 1;
379                                 else
380                                         param.sched_priority = mlt_properties_get_int( properties, "priority" );
381
382                                 pthread_attr_setschedparam(&tattr, &param);
383
384                                 thread = pthread_self();
385
386                                 r = pthread_setschedparam(thread, SCHED_FIFO, &param);
387                                 if( r )
388                                         mlt_log_verbose( getProducer(),
389                                                 "VideoInputFrameArrived: pthread_setschedparam returned %d\n", r);
390                                 else
391                                         mlt_log_verbose( getProducer(),
392                                                 "VideoInputFrameArrived: param.sched_priority=%d\n", param.sched_priority);
393                         };
394
395                         m_reprio = true;
396                 };
397
398                 if ( mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "preview" ) &&
399                         mlt_producer_get_speed( getProducer() ) == 0.0 && !mlt_deque_count( m_queue ))
400                 {
401                         pthread_cond_broadcast( &m_condition );
402                         return S_OK;
403                 }
404
405                 // Create mlt_frame
406                 mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( getProducer() ) );
407
408                 // Copy video
409                 if ( video )
410                 {
411                         if ( !( video->GetFlags() & bmdFrameHasNoInputSource ) )
412                         {
413                                 int size = video->GetRowBytes() * ( video->GetHeight() + m_vancLines );
414                                 void* image = mlt_pool_alloc( size );
415                                 void* buffer = 0;
416                                 unsigned char* p = (unsigned char*) image;
417                                 int n = size / 2;
418 \
419                                 // Initialize VANC lines to nominal black
420                                 while ( --n )
421                                 {
422                                         *p ++ = 16;
423                                         *p ++ = 128;
424                                 }
425
426                                 // Capture VANC
427                                 if ( m_vancLines > 0 )
428                                 {
429                                         IDeckLinkVideoFrameAncillary* vanc = 0;
430                                         if ( video->GetAncillaryData( &vanc ) == S_OK && vanc )
431                                         {
432                                                 for ( int i = 1; i < m_vancLines + 1; i++ )
433                                                 {
434                                                         if ( vanc->GetBufferForVerticalBlankingLine( i, &buffer ) == S_OK )
435                                                                 swab2( (char*) buffer, (char*) image + ( i - 1 ) * video->GetRowBytes(), video->GetRowBytes() );
436                                                         else
437                                                                 mlt_log_debug( getProducer(), "failed capture vanc line %d\n", i );
438                                                 }
439                                                 SAFE_RELEASE(vanc);
440                                         }
441                                 }
442
443                                 // Capture image
444                                 video->GetBytes( &buffer );
445                                 if ( image && buffer )
446                                 {
447                                         size =  video->GetRowBytes() * video->GetHeight();
448                                         swab2( (char*) buffer, (char*) image + m_vancLines * video->GetRowBytes(), size );
449                                         mlt_frame_set_image( frame, (uint8_t*) image, size, mlt_pool_release );
450                                 }
451                                 else if ( image )
452                                 {
453                                         mlt_log_verbose( getProducer(), "no video\n" );
454                                         mlt_pool_release( image );
455                                 }
456                         }
457                         else
458                         {
459                                 mlt_log_verbose( getProducer(), "no signal\n" );
460                                 mlt_frame_close( frame );
461                                 frame = 0;
462                         }
463
464                         // Get timecode
465                         IDeckLinkTimecode* timecode = 0;
466                         if ( video->GetTimecode( bmdTimecodeVITC, &timecode ) == S_OK && timecode )
467                         {
468                                 DLString timecodeString = 0;
469
470                                 if ( timecode->GetString( &timecodeString ) == S_OK )
471                                 {
472                                         char* s = getCString( timecodeString );
473                                         mlt_properties_set( MLT_FRAME_PROPERTIES( frame ), "meta.attr.vitc.markup", s );
474                                         mlt_log_debug( getProducer(), "timecode %s\n", s );
475                                         freeCString( s );
476                                 }
477                                 freeDLString( timecodeString );
478                                 SAFE_RELEASE( timecode );
479                         }
480                 }
481                 else
482                 {
483                         mlt_log_verbose( getProducer(), "no video\n" );
484                         mlt_frame_close( frame );
485                         frame = 0;
486                 }
487
488                 // Copy audio
489                 if ( frame && audio )
490                 {
491                         int channels = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "channels" );
492                         int size = audio->GetSampleFrameCount() * channels * sizeof(int16_t);
493                         mlt_audio_format format = mlt_audio_s16;
494                         void* pcm = mlt_pool_alloc( size );
495                         void* buffer = 0;
496
497                         audio->GetBytes( &buffer );
498                         if ( buffer )
499                         {
500                                 memcpy( pcm, buffer, size );
501                                 mlt_frame_set_audio( frame, pcm, format, size, mlt_pool_release );
502                                 mlt_properties_set_int( MLT_FRAME_PROPERTIES(frame), "audio_samples", audio->GetSampleFrameCount() );
503                         }
504                         else
505                         {
506                                 mlt_log_verbose( getProducer(), "no audio\n" );
507                                 mlt_pool_release( pcm );
508                         }
509                 }
510                 else
511                 {
512                         mlt_log_verbose( getProducer(), "no audio\n" );
513                 }
514
515                 // Put frame in queue
516                 if ( frame )
517                 {
518                         int queueMax = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "buffer" );
519                         pthread_mutex_lock( &m_mutex );
520                         if ( mlt_deque_count( m_queue ) < queueMax )
521                         {
522                                 mlt_deque_push_back( m_queue, frame );
523                                 pthread_cond_broadcast( &m_condition );
524                         }
525                         else
526                         {
527                                 mlt_frame_close( frame );
528                                 mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "dropped", ++m_dropped );
529                                 mlt_log_warning( getProducer(), "buffer overrun, frame dropped %d\n", m_dropped );
530                         }
531                         pthread_mutex_unlock( &m_mutex );
532                 }
533
534                 return S_OK;
535         }
536
537         virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged(
538                         BMDVideoInputFormatChangedEvents events,
539                         IDeckLinkDisplayMode* mode,
540                         BMDDetectedVideoInputFormatFlags flags )
541         {
542                 mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( getProducer() ) );
543                 if ( events & bmdVideoInputDisplayModeChanged )
544                 {
545                         BMDTimeValue duration;
546                         BMDTimeScale timescale;
547                         mode->GetFrameRate( &duration, &timescale );
548                         profile->width = mode->GetWidth();
549                         profile->height = mode->GetHeight() + m_vancLines;
550                         profile->frame_rate_num = timescale;
551                         profile->frame_rate_den = duration;
552                         if ( profile->width == 720 )
553                         {
554                                 if ( profile->height == 576 )
555                                 {
556                                         profile->sample_aspect_num = 16;
557                                         profile->sample_aspect_den = 15;
558                                 }
559                                 else
560                                 {
561                                         profile->sample_aspect_num = 8;
562                                         profile->sample_aspect_den = 9;
563                                 }
564                                 profile->display_aspect_num = 4;
565                                 profile->display_aspect_den = 3;
566                         }
567                         else
568                         {
569                                 profile->sample_aspect_num = 1;
570                                 profile->sample_aspect_den = 1;
571                                 profile->display_aspect_num = 16;
572                                 profile->display_aspect_den = 9;
573                         }
574                         free( profile->description );
575                         profile->description = strdup( "decklink" );
576                         mlt_log_verbose( getProducer(), "format changed %dx%d %.3f fps\n",
577                                 profile->width, profile->height, (double) profile->frame_rate_num / profile->frame_rate_den );
578                 }
579                 if ( events & bmdVideoInputFieldDominanceChanged )
580                 {
581                         profile->progressive = mode->GetFieldDominance() == bmdProgressiveFrame;
582                         m_topFieldFirst = mode->GetFieldDominance() == bmdUpperFieldFirst;
583                         mlt_log_verbose( getProducer(), "field dominance changed prog %d tff %d\n",
584                                 profile->progressive, m_topFieldFirst );
585                 }
586                 if ( events & bmdVideoInputColorspaceChanged )
587                 {
588                         profile->colorspace = m_colorspace =
589                                 ( mode->GetFlags() & bmdDisplayModeColorspaceRec709 ) ? 709 : 601;
590                         mlt_log_verbose( getProducer(), "colorspace changed %d\n", profile->colorspace );
591                 }
592                 return S_OK;
593         }
594 };
595
596 static int get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
597 {
598         return mlt_frame_get_audio( frame, (void**) buffer, format, frequency, channels, samples );
599 }
600
601 static int get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
602 {
603         return mlt_frame_get_image( frame, buffer, format, width, height, writable );
604 }
605
606 static int get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
607 {
608         DeckLinkProducer* decklink = (DeckLinkProducer*) producer->child;
609         mlt_position pos = mlt_producer_position( producer );
610         mlt_position end = mlt_producer_get_playtime( producer );
611         end = ( mlt_producer_get_length( producer ) < end ? mlt_producer_get_length( producer ) : end ) - 1;
612
613         // Re-open if needed
614         if ( !decklink && pos < end )
615         {
616                 producer->child = decklink = new DeckLinkProducer();
617                 decklink->setProducer( producer );
618                 decklink->open( mlt_properties_get_int( MLT_PRODUCER_PROPERTIES(producer), "resource" ) );
619         }
620
621         // Start if needed
622         if ( decklink )
623         {
624                 decklink->start( mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ) );
625
626                 // Get the next frame from the decklink object
627                 if ( ( *frame = decklink->getFrame() ))
628                 {
629                         // Add audio and video getters
630                         mlt_frame_push_audio( *frame, (void*) get_audio );
631                         mlt_frame_push_get_image( *frame, get_image );
632                 }
633         }
634         if ( !*frame )
635                 *frame = mlt_frame_init( MLT_PRODUCER_SERVICE(producer) );
636
637         // Calculate the next timecode
638         mlt_producer_prepare_next( producer );
639
640         // Close DeckLink if at end
641         if ( pos >= end && decklink )
642         {
643                 decklink->stop();
644                 delete decklink;
645                 producer->child = NULL;
646         }
647
648         return 0;
649 }
650
651 static void producer_close( mlt_producer producer )
652 {
653         delete (DeckLinkProducer*) producer->child;
654         producer->close = NULL;
655         mlt_producer_close( producer );
656 }
657
658 extern "C" {
659
660 // Listen for the list_devices property to be set
661 static void on_property_changed( void*, mlt_properties properties, const char *name )
662 {
663         IDeckLinkIterator* decklinkIterator = NULL;
664         IDeckLink* decklink = NULL;
665         IDeckLinkInput* decklinkInput = NULL;
666         int i = 0;
667
668         if ( name && !strcmp( name, "list_devices" ) )
669                 mlt_event_block( (mlt_event) mlt_properties_get_data( properties, "list-devices-event", NULL ) );
670         else
671                 return;
672
673 #ifdef WIN32
674         if ( FAILED( CoInitialize( NULL ) ) )
675                 return;
676         if ( FAILED( CoCreateInstance( CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**) &decklinkIterator ) ) )
677                 return;
678 #else
679         if ( !( decklinkIterator = CreateDeckLinkIteratorInstance() ) )
680                 return;
681 #endif
682         for ( ; decklinkIterator->Next( &decklink ) == S_OK; i++ )
683         {
684                 if ( decklink->QueryInterface( IID_IDeckLinkInput, (void**) &decklinkInput ) == S_OK )
685                 {
686                         DLString name = NULL;
687                         if ( decklink->GetModelName( &name ) == S_OK )
688                         {
689                                 char *name_cstr = getCString( name );
690                                 const char *format = "device.%d";
691                                 char *key = (char*) calloc( 1, strlen( format ) + 1 );
692
693                                 sprintf( key, format, i );
694                                 mlt_properties_set( properties, key, name_cstr );
695                                 free( key );
696                                 freeDLString( name );
697                                 freeCString( name_cstr );
698                         }
699                         SAFE_RELEASE( decklinkInput );
700                 }
701                 SAFE_RELEASE( decklink );
702         }
703         SAFE_RELEASE( decklinkIterator );
704         mlt_properties_set_int( properties, "devices", i );
705 }
706
707 /** Initialise the producer.
708  */
709
710 mlt_producer producer_decklink_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
711 {
712         // Allocate the producer
713         DeckLinkProducer* decklink = new DeckLinkProducer();
714         mlt_producer producer = (mlt_producer) calloc( 1, sizeof( *producer ) );
715
716         // If allocated and initializes
717         if ( decklink && !mlt_producer_init( producer, decklink ) )
718         {
719                 if ( decklink->open( arg? atoi( arg ) : 0 ) )
720                 {
721                         mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
722
723                         // Close DeckLink and defer re-open to get_frame
724                         delete decklink;
725                         producer->child = NULL;
726
727                         // Set callbacks
728                         producer->close = (mlt_destructor) producer_close;
729                         producer->get_frame = get_frame;
730
731                         // Set properties
732                         mlt_properties_set( properties, "resource", (arg && strcmp( arg, ""))? arg : "0" );
733                         mlt_properties_set_int( properties, "channels", 2 );
734                         mlt_properties_set_int( properties, "buffer", 25 );
735                         mlt_properties_set_int( properties, "prefill", 25 );
736
737                         // These properties effectively make it infinite.
738                         mlt_properties_set_int( properties, "length", INT_MAX );
739                         mlt_properties_set_int( properties, "out", INT_MAX - 1 );
740                         mlt_properties_set( properties, "eof", "loop" );
741
742                         mlt_event event = mlt_events_listen( properties, properties, "property-changed", (mlt_listener) on_property_changed );
743                         mlt_properties_set_data( properties, "list-devices-event", event, 0, NULL, NULL );
744                 }
745         }
746
747         return producer;
748 }
749
750 }