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