]> git.sesse.net Git - mlt/blob - src/modules/decklink/producer_decklink.cpp
Fix deadlock on no signal in decklink producer.
[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 "DeckLinkAPI.h"
28
29 class DeckLinkProducer
30         : public IDeckLinkInputCallback
31 {
32 private:
33         mlt_producer_s   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
42         BMDDisplayMode getDisplayMode( mlt_profile profile )
43         {
44                 IDeckLinkDisplayModeIterator* iter;
45                 IDeckLinkDisplayMode* mode;
46                 BMDDisplayMode result = bmdDisplayModeNotSupported;
47
48                 if ( m_decklinkInput->GetDisplayModeIterator( &iter ) == S_OK )
49                 {
50                         while ( !result && iter->Next( &mode ) == S_OK )
51                         {
52                                 int width = mode->GetWidth();
53                                 int height = mode->GetHeight();
54                                 BMDTimeValue duration;
55                                 BMDTimeScale timescale;
56                                 mode->GetFrameRate( &duration, &timescale );
57                                 double fps = (double) timescale / duration;
58                                 int p = mode->GetFieldDominance() == bmdProgressiveFrame;
59                                 mlt_log_verbose( getProducer(), "BMD mode %dx%d %.3f fps prog %d\n", width, height, fps, p );
60
61                                 if ( width == profile->width && height == profile->height && p == profile->progressive
62                                          && fps == mlt_profile_fps( profile ) )
63                                         result = mode->GetDisplayMode();
64                         }
65                 }
66
67                 return result;
68         }
69
70 public:
71
72         mlt_producer getProducer()
73                 { return &m_producer; }
74
75         ~DeckLinkProducer()
76         {
77                 if ( m_decklinkInput )
78                         m_decklinkInput->Release();
79                 if ( m_decklink )
80                         m_decklink->Release();
81                 if ( m_queue )
82                 {
83                         stop();
84                         mlt_deque_close( m_queue );
85                         pthread_mutex_destroy( &m_mutex );
86                         pthread_cond_destroy( &m_condition );
87                 }
88         }
89
90         bool open( mlt_profile profile, unsigned card =  0 )
91         {
92                 IDeckLinkIterator* decklinkIterator = CreateDeckLinkIteratorInstance();
93                 try
94                 {
95                         if ( !decklinkIterator )
96                                 throw "The DeckLink drivers are not installed.";
97
98                         // Connect to the Nth DeckLink instance
99                         unsigned i = 0;
100                         do {
101                                 if ( decklinkIterator->Next( &m_decklink ) != S_OK )
102                                         throw "DeckLink card not found.";
103                         } while ( ++i <= card );
104                         decklinkIterator->Release();
105
106                         // Get the input interface
107                         if ( m_decklink->QueryInterface( IID_IDeckLinkInput, (void**) &m_decklinkInput ) != S_OK )
108                                 throw "No DeckLink cards support input.";
109
110                         // Provide this class as a delegate to the input callback
111                         m_decklinkInput->SetCallback( this );
112
113                         // Initialize other members
114                         pthread_mutex_init( &m_mutex, NULL );
115                         pthread_cond_init( &m_condition, NULL );
116                         m_queue = mlt_deque_init();
117                         m_started = false;
118                         m_dropped = 0;
119                 }
120                 catch ( const char *error )
121                 {
122                         if ( decklinkIterator )
123                                 decklinkIterator->Release();
124                         mlt_log_error( getProducer(), "%s\n", error );
125                         return false;
126                 }
127                 return true;
128         }
129
130         bool start( mlt_profile profile = 0 )
131         {
132                 if ( m_started )
133                         return false;
134                 try
135                 {
136                         if ( !profile )
137                                 profile = mlt_service_profile( MLT_PRODUCER_SERVICE( getProducer() ) );
138
139                         // Get the display mode
140                         BMDDisplayMode displayMode = getDisplayMode( profile );
141                         if ( displayMode == bmdDisplayModeNotSupported )
142                                 throw "Profile is not compatible with decklink.";
143
144                         // Enable video capture
145                         BMDPixelFormat pixelFormat = bmdFormat8BitYUV;
146                         BMDVideoInputFlags flags = bmdVideoInputFlagDefault;
147                         if ( S_OK != m_decklinkInput->EnableVideoInput( displayMode, pixelFormat, flags ) )
148                                 throw "Failed to enable video capture.";
149
150                         // Enable audio capture
151                         BMDAudioSampleRate sampleRate = bmdAudioSampleRate48kHz;
152                         BMDAudioSampleType sampleType = bmdAudioSampleType16bitInteger;
153                         int channels = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "channels" );
154                         if ( S_OK != m_decklinkInput->EnableAudioInput( sampleRate, sampleType, channels ) )
155                                 throw "Failed to enable audio capture.";
156
157                         // Start capture
158                         m_dropped = 0;
159                         mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "dropped", m_dropped );
160                         m_started = m_decklinkInput->StartStreams() == S_OK;
161                         if ( !m_started )
162                                 throw "Failed to start capture.";
163                 }
164                 catch ( const char *error )
165                 {
166                         m_decklinkInput->DisableVideoInput();
167                         mlt_log_error( getProducer(), "%s\n", error );
168                         return false;
169                 }
170                 return true;
171         }
172
173         void stop()
174         {
175                 if ( m_started )
176                         return;
177
178                 // Release the wait in getFrame
179                 pthread_mutex_lock( &m_mutex );
180                 pthread_cond_broadcast( &m_condition );
181                 pthread_mutex_unlock( &m_mutex );
182
183                 m_decklinkInput->StopStreams();
184
185                 // Cleanup queue
186                 pthread_mutex_lock( &m_mutex );
187                 while ( mlt_frame frame = (mlt_frame) mlt_deque_pop_back( m_queue ) )
188                         mlt_frame_close( frame );
189                 pthread_mutex_unlock( &m_mutex );
190
191                 m_started = false;
192         }
193
194         mlt_frame getFrame()
195         {
196                 mlt_frame frame = NULL;
197                 struct timeval now;
198                 struct timespec tm;
199
200                 // Wait if queue is empty
201                 pthread_mutex_lock( &m_mutex );
202                 while ( mlt_deque_count( m_queue ) < 1 )
203                 {
204                         // Wait up to twice frame duration
205                         gettimeofday( &now, NULL );
206                         tm.tv_sec = now.tv_sec;
207                         now.tv_usec += 2000000 / mlt_producer_get_fps( getProducer() );
208                         tm.tv_nsec = now.tv_usec * 1000;
209                         if ( pthread_cond_timedwait( &m_condition, &m_mutex, &tm ) )
210                                 // Stop waiting if error (timed out)
211                                 break;
212                 }
213
214                 // Get the first frame from the queue
215                 frame = ( mlt_frame ) mlt_deque_pop_front( m_queue );
216                 pthread_mutex_unlock( &m_mutex );
217
218                 // Set frame timestamp and properties
219                 if ( frame )
220                 {
221                         mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( getProducer() ) );
222                         mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
223                         mlt_properties_set_int( properties, "progressive", profile->progressive );
224                         mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) );
225                         mlt_properties_set_int( properties, "width", profile->width );
226                         mlt_properties_set_int( properties, "real_width", profile->width );
227                         mlt_properties_set_int( properties, "height", profile->height );
228                         mlt_properties_set_int( properties, "real_height", profile->height );
229                         mlt_properties_set_int( properties, "format", mlt_image_yuv422 );
230                         mlt_properties_set_int( properties, "audio_frequency", 48000 );
231                         mlt_properties_set_int( properties, "audio_channels",
232                                 mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "channels" ) );
233                 }
234                 return frame;
235         }
236
237         // *** DeckLink API implementation of IDeckLinkInputCallback *** //
238
239         // IUnknown needs only a dummy implementation
240         virtual HRESULT STDMETHODCALLTYPE QueryInterface( REFIID iid, LPVOID *ppv )
241                 { return E_NOINTERFACE; }
242         virtual ULONG STDMETHODCALLTYPE AddRef()
243                 { return 1; }
244         virtual ULONG STDMETHODCALLTYPE Release()
245                 { return 1; }
246
247         /************************* DeckLink API Delegate Methods *****************************/
248
249         virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(
250                         IDeckLinkVideoInputFrame* video,
251                         IDeckLinkAudioInputPacket* audio )
252         {
253                 // Create mlt_frame
254                 mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( getProducer() ) );
255
256                 // Copy video
257                 if ( video )
258                 {
259                         if ( !( video->GetFlags() & bmdFrameHasNoInputSource ) )
260                         {
261                                 int size = video->GetRowBytes() * video->GetHeight();
262                                 void* image = mlt_pool_alloc( size );
263                                 void* buffer = 0;
264
265                                 video->GetBytes( &buffer );
266                                 if ( image && buffer )
267                                 {
268                                         swab( buffer, image, size );
269                                         mlt_frame_set_image( frame, (uint8_t*) image, size, mlt_pool_release );
270                                 }
271                                 else if ( image )
272                                 {
273                                         mlt_log_verbose( getProducer(), "no video\n" );
274                                         mlt_pool_release( image );
275                                 }
276                         }
277                         else
278                         {
279                                 mlt_log_verbose( getProducer(), "no signal\n" );
280                                 mlt_frame_close( frame );
281                                 frame = 0;
282                         }
283                 }
284                 else
285                 {
286                         mlt_log_verbose( getProducer(), "no video\n" );
287                         mlt_frame_close( frame );
288                         frame = 0;
289                 }
290
291                 // Copy audio
292                 if ( frame && audio )
293                 {
294                         int channels = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "channels" );
295                         int size = audio->GetSampleFrameCount() * channels * sizeof(int16_t);
296                         mlt_audio_format format = mlt_audio_s16;
297                         void* pcm = mlt_pool_alloc( size );
298                         void* buffer = 0;
299
300                         audio->GetBytes( &buffer );
301                         if ( buffer )
302                         {
303                                 memcpy( pcm, buffer, size );
304                                 mlt_frame_set_audio( frame, pcm, format, size, mlt_pool_release );
305                                 mlt_properties_set_int( MLT_FRAME_PROPERTIES(frame), "audio_samples", audio->GetSampleFrameCount() );
306                         }
307                         else
308                         {
309                                 mlt_log_verbose( getProducer(), "no audio\n" );
310                                 mlt_pool_release( pcm );
311                         }
312                 }
313                 else
314                 {
315                         mlt_log_verbose( getProducer(), "no audio\n" );
316                 }
317
318                 // Put frame in queue
319                 if ( frame )
320                 {
321                         int queueMax = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "buffer" );
322                         pthread_mutex_lock( &m_mutex );
323                         if ( mlt_deque_count( m_queue ) < queueMax )
324                         {
325                                 mlt_deque_push_back( m_queue, frame );
326                                 pthread_cond_broadcast( &m_condition );
327                         }
328                         else
329                         {
330                                 mlt_frame_close( frame );
331                                 mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "dropped", ++m_dropped );
332                                 mlt_log_warning( getProducer(), "frame dropped %d\n", m_dropped );
333                         }
334                         pthread_mutex_unlock( &m_mutex );
335                 }
336
337                 return S_OK;
338         }
339
340         virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged(
341                         BMDVideoInputFormatChangedEvents events,
342                         IDeckLinkDisplayMode* mode,
343                         BMDDetectedVideoInputFormatFlags flags )
344         {
345                 return S_OK;
346         }
347 };
348
349 static int get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
350 {
351         return mlt_frame_get_audio( frame, (void**) buffer, format, frequency, channels, samples );
352 }
353
354 static int get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
355 {
356         return mlt_frame_get_image( frame, buffer, format, width, height, writable );
357 }
358
359 static int get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
360 {
361         DeckLinkProducer* decklink = (DeckLinkProducer*) producer->child;
362
363         // Get the next frame from the decklink object
364         *frame = decklink->getFrame();
365         if ( !*frame )
366                 *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
367
368         // Calculate the next timecode
369         mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
370         mlt_producer_prepare_next( producer );
371
372         // Add audio and video getters
373         mlt_frame_push_audio( *frame, (void*) get_audio );
374         mlt_frame_push_get_image( *frame, get_image );
375
376         return 0;
377 }
378
379 static void producer_close( mlt_producer producer )
380 {
381         producer->close = NULL;
382         mlt_producer_close( producer );
383         delete (DeckLinkProducer*) producer->child;
384 }
385
386 extern "C" {
387
388 /** Initialise the producer.
389  */
390
391 mlt_producer producer_decklink_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
392 {
393         // Allocate the producer
394         DeckLinkProducer* decklink = new DeckLinkProducer();
395         mlt_producer producer = NULL;
396
397         // If allocated and initializes
398         if ( decklink && !mlt_producer_init( decklink->getProducer(), decklink ) )
399         {
400                 if ( decklink->open( profile, arg? atoi( arg ) : 0 ) )
401                 {
402                         producer = decklink->getProducer();
403                         mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
404
405                         // Set callbacks
406                         producer->close = (mlt_destructor) producer_close;
407                         producer->get_frame = get_frame;
408
409                         // Set properties
410                         mlt_properties_set( properties, "resource", arg? arg : "0" );
411                         mlt_properties_set_int( properties, "channels", 2 );
412                         mlt_properties_set_int( properties, "buffer", 25 );
413                         mlt_properties_set_int( properties, "length", INT_MAX );
414                         mlt_properties_set_int( properties, "out", INT_MAX - 1 );
415                         mlt_properties_set( properties, "eof", "loop" );
416
417                         // Start immediately
418                         if ( !decklink->start( profile ) )
419                         {
420                                 producer_close( producer );
421                                 producer = NULL;
422                         }
423                 }
424         }
425
426         return producer;
427 }
428
429 }