]> git.sesse.net Git - mlt/blob - src/modules/decklink/producer_decklink.cpp
Add vanc property to 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 #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
221                 // Release the wait in getFrame
222                 pthread_mutex_lock( &m_mutex );
223                 pthread_cond_broadcast( &m_condition );
224                 pthread_mutex_unlock( &m_mutex );
225
226                 m_decklinkInput->StopStreams();
227
228                 // Cleanup queue
229                 pthread_mutex_lock( &m_mutex );
230                 while ( mlt_frame frame = (mlt_frame) mlt_deque_pop_back( m_queue ) )
231                         mlt_frame_close( frame );
232                 pthread_mutex_unlock( &m_mutex );
233
234                 m_started = false;
235         }
236
237         mlt_frame getFrame()
238         {
239                 mlt_frame frame = NULL;
240                 struct timeval now;
241                 struct timespec tm;
242                 double fps = mlt_producer_get_fps( getProducer() );
243
244                 // Allow the buffer to fill to the requested initial buffer level.
245                 if ( m_isBuffering )
246                 {
247                         int prefill = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "prefill" );
248                         int buffer = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "buffer" );
249
250                         m_isBuffering = false;
251                         prefill = prefill > buffer ? buffer : prefill;
252                         pthread_mutex_lock( &m_mutex );
253                         while ( mlt_deque_count( m_queue ) < prefill )
254                         {
255                                 // Wait up to buffer/fps seconds
256                                 gettimeofday( &now, NULL );
257                                 long usec = now.tv_sec * 1000000 + now.tv_usec;
258                                 usec += 1000000 * buffer / fps;
259                                 tm.tv_sec = usec / 1000000;
260                                 tm.tv_nsec = (usec % 1000000) * 1000;
261                                 if ( pthread_cond_timedwait( &m_condition, &m_mutex, &tm ) )
262                                         break;
263                         }
264                         pthread_mutex_unlock( &m_mutex );
265                 }
266
267                 // Wait if queue is empty
268                 pthread_mutex_lock( &m_mutex );
269                 while ( mlt_deque_count( m_queue ) < 1 )
270                 {
271                         // Wait up to twice frame duration
272                         gettimeofday( &now, NULL );
273                         long usec = now.tv_sec * 1000000 + now.tv_usec;
274                         usec += 2000000 / fps;
275                         tm.tv_sec = usec / 1000000;
276                         tm.tv_nsec = (usec % 1000000) * 1000;
277                         if ( pthread_cond_timedwait( &m_condition, &m_mutex, &tm ) )
278                                 // Stop waiting if error (timed out)
279                                 break;
280                 }
281
282                 // Get the first frame from the queue
283                 frame = ( mlt_frame ) mlt_deque_pop_front( m_queue );
284                 pthread_mutex_unlock( &m_mutex );
285
286                 // Set frame timestamp and properties
287                 if ( frame )
288                 {
289                         mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( getProducer() ) );
290                         mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
291                         mlt_properties_set_int( properties, "progressive", profile->progressive );
292                         mlt_properties_set_int( properties, "meta.media.progressive", profile->progressive );
293                         mlt_properties_set_int( properties, "top_field_first", m_topFieldFirst );
294                         mlt_properties_set_double( properties, "aspect_ratio", mlt_profile_sar( profile ) );
295                         mlt_properties_set_int( properties, "meta.media.sample_aspect_num", profile->sample_aspect_num );
296                         mlt_properties_set_int( properties, "meta.media.sample_aspect_den", profile->sample_aspect_den );
297                         mlt_properties_set_int( properties, "meta.media.frame_rate_num", profile->frame_rate_num );
298                         mlt_properties_set_int( properties, "meta.media.frame_rate_den", profile->frame_rate_den );
299                         mlt_properties_set_int( properties, "width", profile->width );
300                         mlt_properties_set_int( properties, "real_width", profile->width );
301                         mlt_properties_set_int( properties, "meta.media.width", profile->width );
302                         mlt_properties_set_int( properties, "height", profile->height );
303                         mlt_properties_set_int( properties, "real_height", profile->height );
304                         mlt_properties_set_int( properties, "meta.media.height", profile->height );
305                         mlt_properties_set_int( properties, "format", mlt_image_yuv422 );
306                         mlt_properties_set_int( properties, "colorspace", m_colorspace );
307                         mlt_properties_set_int( properties, "meta.media.colorspace", m_colorspace );
308                         mlt_properties_set_int( properties, "audio_frequency", 48000 );
309                         mlt_properties_set_int( properties, "audio_channels",
310                                 mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "channels" ) );
311                 }
312                 return frame;
313         }
314
315         // *** DeckLink API implementation of IDeckLinkInputCallback *** //
316
317         // IUnknown needs only a dummy implementation
318         virtual HRESULT STDMETHODCALLTYPE QueryInterface( REFIID iid, LPVOID *ppv )
319                 { return E_NOINTERFACE; }
320         virtual ULONG STDMETHODCALLTYPE AddRef()
321                 { return 1; }
322         virtual ULONG STDMETHODCALLTYPE Release()
323                 { return 1; }
324
325         /************************* DeckLink API Delegate Methods *****************************/
326
327         virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(
328                         IDeckLinkVideoInputFrame* video,
329                         IDeckLinkAudioInputPacket* audio )
330         {
331                 // Create mlt_frame
332                 mlt_frame frame = mlt_frame_init( MLT_PRODUCER_SERVICE( getProducer() ) );
333
334                 // Copy video
335                 if ( video )
336                 {
337                         if ( !( video->GetFlags() & bmdFrameHasNoInputSource ) )
338                         {
339                                 int size = video->GetRowBytes() * ( video->GetHeight() + m_vancLines );
340                                 void* image = mlt_pool_alloc( size );
341                                 void* buffer = 0;
342                                 unsigned char* p = (unsigned char*) image;
343                                 int n = size / 2;
344 \
345                                 // Initialize VANC lines to nominal black
346                                 while ( --n )
347                                 {
348                                         *p ++ = 16;
349                                         *p ++ = 128;
350                                 }
351
352                                 // Capture VANC
353                                 if ( m_vancLines > 0 )
354                                 {
355                                         IDeckLinkVideoFrameAncillary* vanc = 0;
356                                         if ( video->GetAncillaryData( &vanc ) == S_OK && vanc )
357                                         {
358                                                 for ( int i = 1; i < m_vancLines + 1; i++ )
359                                                 {
360                                                         if ( vanc->GetBufferForVerticalBlankingLine( i, &buffer ) == S_OK )
361                                                                 swab( (char*) buffer, (char*) image + ( i - 1 ) * video->GetRowBytes(), video->GetRowBytes() );
362                                                         else
363                                                                 mlt_log_debug( getProducer(), "failed capture vanc line %d\n", i );
364                                                 }
365                                                 vanc->Release();
366                                         }
367                                 }
368
369                                 // Capture image
370                                 video->GetBytes( &buffer );
371                                 if ( image && buffer )
372                                 {
373                                         size =  video->GetRowBytes() * video->GetHeight();
374                                         swab( (char*) buffer, (char*) image + m_vancLines * video->GetRowBytes(), size );
375                                         mlt_frame_set_image( frame, (uint8_t*) image, size, mlt_pool_release );
376                                 }
377                                 else if ( image )
378                                 {
379                                         mlt_log_verbose( getProducer(), "no video\n" );
380                                         mlt_pool_release( image );
381                                 }
382                         }
383                         else
384                         {
385                                 mlt_log_verbose( getProducer(), "no signal\n" );
386                                 mlt_frame_close( frame );
387                                 frame = 0;
388                         }
389
390                         // Get timecode
391                         IDeckLinkTimecode* timecode = 0;
392                         if ( video->GetTimecode( bmdTimecodeVITC, &timecode ) == S_OK && timecode )
393                         {
394                                 const char* timecodeString = 0;
395
396                                 if ( timecode->GetString( &timecodeString ) == S_OK )
397                                 {
398                                         mlt_properties_set( MLT_FRAME_PROPERTIES( frame ), "meta.attr.vitc.markup", timecodeString );
399                                         mlt_log_debug( getProducer(), "timecode %s\n", timecodeString );
400                                 }
401                                 if ( timecodeString )
402                                         free( (void*) timecodeString );
403                                 timecode->Release();
404                         }
405                 }
406                 else
407                 {
408                         mlt_log_verbose( getProducer(), "no video\n" );
409                         mlt_frame_close( frame );
410                         frame = 0;
411                 }
412
413                 // Copy audio
414                 if ( frame && audio )
415                 {
416                         int channels = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "channels" );
417                         int size = audio->GetSampleFrameCount() * channels * sizeof(int16_t);
418                         mlt_audio_format format = mlt_audio_s16;
419                         void* pcm = mlt_pool_alloc( size );
420                         void* buffer = 0;
421
422                         audio->GetBytes( &buffer );
423                         if ( buffer )
424                         {
425                                 memcpy( pcm, buffer, size );
426                                 mlt_frame_set_audio( frame, pcm, format, size, mlt_pool_release );
427                                 mlt_properties_set_int( MLT_FRAME_PROPERTIES(frame), "audio_samples", audio->GetSampleFrameCount() );
428                         }
429                         else
430                         {
431                                 mlt_log_verbose( getProducer(), "no audio\n" );
432                                 mlt_pool_release( pcm );
433                         }
434                 }
435                 else
436                 {
437                         mlt_log_verbose( getProducer(), "no audio\n" );
438                 }
439
440                 // Put frame in queue
441                 if ( frame )
442                 {
443                         int queueMax = mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "buffer" );
444                         pthread_mutex_lock( &m_mutex );
445                         if ( mlt_deque_count( m_queue ) < queueMax )
446                         {
447                                 mlt_deque_push_back( m_queue, frame );
448                                 pthread_cond_broadcast( &m_condition );
449                         }
450                         else
451                         {
452                                 mlt_frame_close( frame );
453                                 mlt_properties_set_int( MLT_PRODUCER_PROPERTIES( getProducer() ), "dropped", ++m_dropped );
454                                 mlt_log_warning( getProducer(), "frame dropped %d\n", m_dropped );
455                         }
456                         pthread_mutex_unlock( &m_mutex );
457                 }
458
459                 return S_OK;
460         }
461
462         virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged(
463                         BMDVideoInputFormatChangedEvents events,
464                         IDeckLinkDisplayMode* mode,
465                         BMDDetectedVideoInputFormatFlags flags )
466         {
467                 mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( getProducer() ) );
468                 if ( events & bmdVideoInputDisplayModeChanged )
469                 {
470                         BMDTimeValue duration;
471                         BMDTimeScale timescale;
472                         mode->GetFrameRate( &duration, &timescale );
473                         profile->width = mode->GetWidth();
474                         profile->height = mode->GetHeight() + m_vancLines;
475                         profile->frame_rate_num = timescale;
476                         profile->frame_rate_den = duration;
477                         if ( profile->width == 720 )
478                         {
479                                 if ( profile->height == 576 )
480                                 {
481                                         profile->sample_aspect_num = 16;
482                                         profile->sample_aspect_den = 15;
483                                 }
484                                 else
485                                 {
486                                         profile->sample_aspect_num = 8;
487                                         profile->sample_aspect_den = 9;
488                                 }
489                                 profile->display_aspect_num = 4;
490                                 profile->display_aspect_den = 3;
491                         }
492                         else
493                         {
494                                 profile->sample_aspect_num = 1;
495                                 profile->sample_aspect_den = 1;
496                                 profile->display_aspect_num = 16;
497                                 profile->display_aspect_den = 9;
498                         }
499                         free( profile->description );
500                         profile->description = strdup( "decklink" );
501                         mlt_log_verbose( getProducer(), "format changed %dx%d %.3f fps\n",
502                                 profile->width, profile->height, (double) profile->frame_rate_num / profile->frame_rate_den );
503                 }
504                 if ( events & bmdVideoInputFieldDominanceChanged )
505                 {
506                         profile->progressive = mode->GetFieldDominance() == bmdProgressiveFrame;
507                         m_topFieldFirst = mode->GetFieldDominance() == bmdUpperFieldFirst;
508                         mlt_log_verbose( getProducer(), "field dominance changed prog %d tff %d\n",
509                                 profile->progressive, m_topFieldFirst );
510                 }
511                 if ( events & bmdVideoInputColorspaceChanged )
512                 {
513                         profile->colorspace = m_colorspace =
514                                 ( mode->GetFlags() & bmdDisplayModeColorspaceRec709 ) ? 709 : 601;
515                         mlt_log_verbose( getProducer(), "colorspace changed %d\n", profile->colorspace );
516                 }
517                 return S_OK;
518         }
519 };
520
521 static int get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
522 {
523         return mlt_frame_get_audio( frame, (void**) buffer, format, frequency, channels, samples );
524 }
525
526 static int get_image( mlt_frame frame, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
527 {
528         return mlt_frame_get_image( frame, buffer, format, width, height, writable );
529 }
530
531 static int get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
532 {
533         DeckLinkProducer* decklink = (DeckLinkProducer*) producer->child;
534
535         // Start if needed
536         decklink->start( mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) ) );
537
538         // Get the next frame from the decklink object
539         *frame = decklink->getFrame();
540         if ( !*frame )
541                 *frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
542
543         // Calculate the next timecode
544         mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
545         mlt_producer_prepare_next( producer );
546
547         // Add audio and video getters
548         mlt_frame_push_audio( *frame, (void*) get_audio );
549         mlt_frame_push_get_image( *frame, get_image );
550
551         return 0;
552 }
553
554 static void producer_close( mlt_producer producer )
555 {
556         producer->close = NULL;
557         mlt_producer_close( producer );
558         delete (DeckLinkProducer*) producer->child;
559 }
560
561 extern "C" {
562
563 /** Initialise the producer.
564  */
565
566 mlt_producer producer_decklink_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
567 {
568         // Allocate the producer
569         DeckLinkProducer* decklink = new DeckLinkProducer();
570         mlt_producer producer = NULL;
571
572         // If allocated and initializes
573         if ( decklink && !mlt_producer_init( decklink->getProducer(), decklink ) )
574         {
575                 if ( decklink->open( profile, arg? atoi( arg ) : 0 ) )
576                 {
577                         producer = decklink->getProducer();
578                         mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
579
580                         // Set callbacks
581                         producer->close = (mlt_destructor) producer_close;
582                         producer->get_frame = get_frame;
583
584                         // Set properties
585                         mlt_properties_set( properties, "resource", arg? arg : "0" );
586                         mlt_properties_set_int( properties, "channels", 2 );
587                         mlt_properties_set_int( properties, "buffer", 25 );
588                         mlt_properties_set_int( properties, "prefill", 25 );
589
590                         // These properties effectively make it infinite.
591                         mlt_properties_set_int( properties, "length", INT_MAX );
592                         mlt_properties_set_int( properties, "out", INT_MAX - 1 );
593                         mlt_properties_set( properties, "eof", "loop" );
594                 }
595         }
596
597         return producer;
598 }
599
600 }