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