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