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