1 /*
2 * consumer_decklink.cpp -- output through Blackmagic Design DeckLink
3 * Copyright (C) 2010-2018 Meltytech, LLC
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #define __STDC_FORMAT_MACROS /* see inttypes.h */
21 #include <framework/mlt.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <pthread.h>
25 #include <unistd.h>
26 #include <sys/time.h>
27 #include <limits.h>
28 #include <pthread.h>
29 #include "common.h"
30
31 #define SWAB_SLICED_ALIGN_POW 5
swab_sliced(int id,int idx,int jobs,void * cookie)32 static int swab_sliced( int id, int idx, int jobs, void* cookie )
33 {
34 unsigned char** args = (unsigned char**)cookie;
35 ssize_t sz = (ssize_t)args[2];
36 ssize_t bsz = ( ( sz / jobs + ( 1 << SWAB_SLICED_ALIGN_POW ) - 1 ) >> SWAB_SLICED_ALIGN_POW ) << SWAB_SLICED_ALIGN_POW;
37 ssize_t offset = bsz * idx;
38
39 if ( offset < sz )
40 {
41 if ( ( offset + bsz ) > sz )
42 bsz = sz - offset;
43
44 swab2( args[0] + offset, args[1] + offset, bsz );
45 }
46
47 return 0;
48 };
49
50 static const unsigned PREROLL_MINIMUM = 3;
51
52 enum
53 {
54 OP_NONE = 0,
55 OP_OPEN,
56 OP_START,
57 OP_STOP,
58 OP_EXIT
59 };
60
61 class DeckLinkConsumer
62 : public IDeckLinkVideoOutputCallback
63 , public IDeckLinkAudioOutputCallback
64 {
65 private:
66 mlt_consumer_s m_consumer;
67 IDeckLink* m_deckLink;
68 IDeckLinkOutput* m_deckLinkOutput;
69 IDeckLinkDisplayMode* m_displayMode;
70 int m_width;
71 int m_height;
72 BMDTimeValue m_duration;
73 BMDTimeScale m_timescale;
74 double m_fps;
75 uint64_t m_count;
76 int m_outChannels;
77 int m_inChannels;
78 bool m_isAudio;
79 int m_isKeyer;
80 IDeckLinkKeyer* m_deckLinkKeyer;
81 bool m_terminate_on_pause;
82 uint32_t m_preroll;
83 uint32_t m_reprio;
84
85 mlt_deque m_aqueue;
86 pthread_mutex_t m_aqueue_lock;
87 mlt_deque m_frames;
88
89 pthread_mutex_t m_op_lock;
90 pthread_mutex_t m_op_arg_mutex;
91 pthread_cond_t m_op_arg_cond;
92 int m_op_id;
93 int m_op_res;
94 int m_op_arg;
95 pthread_t m_op_thread;
96 bool m_sliced_swab;
97 uint8_t* m_buffer;
98
getDisplayMode()99 IDeckLinkDisplayMode* getDisplayMode()
100 {
101 mlt_profile profile = mlt_service_profile( MLT_CONSUMER_SERVICE( getConsumer() ) );
102 IDeckLinkDisplayModeIterator* iter = NULL;
103 IDeckLinkDisplayMode* mode = NULL;
104 IDeckLinkDisplayMode* result = 0;
105
106 if ( m_deckLinkOutput->GetDisplayModeIterator( &iter ) == S_OK )
107 {
108 while ( !result && iter->Next( &mode ) == S_OK )
109 {
110 m_width = mode->GetWidth();
111 m_height = mode->GetHeight();
112 mode->GetFrameRate( &m_duration, &m_timescale );
113 m_fps = (double) m_timescale / m_duration;
114 int p = mode->GetFieldDominance() == bmdProgressiveFrame;
115 mlt_log_verbose( getConsumer(), "BMD mode %dx%d %.3f fps prog %d\n", m_width, m_height, m_fps, p );
116
117 if ( m_width == profile->width && p == profile->progressive
118 && (int) m_fps == (int) mlt_profile_fps( profile )
119 && ( m_height == profile->height || ( m_height == 486 && profile->height == 480 ) ) )
120 result = mode;
121 else
122 SAFE_RELEASE( mode );
123 }
124 SAFE_RELEASE( iter );
125 }
126
127 return result;
128 }
129
130 public:
getConsumer()131 mlt_consumer getConsumer()
132 { return &m_consumer; }
133
DeckLinkConsumer()134 DeckLinkConsumer()
135 {
136 pthread_mutexattr_t mta;
137
138 m_displayMode = NULL;
139 m_deckLinkKeyer = NULL;
140 m_deckLinkOutput = NULL;
141 m_deckLink = NULL;
142 m_aqueue = mlt_deque_init();
143 m_frames = mlt_deque_init();
144 m_buffer = NULL;
145
146 // operation locks
147 m_op_id = OP_NONE;
148 m_op_arg = 0;
149 pthread_mutexattr_init( &mta );
150 pthread_mutexattr_settype( &mta, PTHREAD_MUTEX_RECURSIVE );
151 pthread_mutex_init( &m_op_lock, &mta );
152 pthread_mutex_init( &m_op_arg_mutex, &mta );
153 pthread_mutex_init( &m_aqueue_lock, &mta );
154 pthread_mutexattr_destroy( &mta );
155 pthread_cond_init( &m_op_arg_cond, NULL );
156 pthread_create( &m_op_thread, NULL, op_main, this );
157 }
158
~DeckLinkConsumer()159 virtual ~DeckLinkConsumer()
160 {
161 mlt_log_debug( getConsumer(), "%s: entering\n", __FUNCTION__ );
162
163 SAFE_RELEASE( m_displayMode );
164 SAFE_RELEASE( m_deckLinkKeyer );
165 SAFE_RELEASE( m_deckLinkOutput );
166 SAFE_RELEASE( m_deckLink );
167
168 mlt_deque_close( m_aqueue );
169 mlt_deque_close( m_frames );
170
171 op(OP_EXIT, 0);
172 mlt_log_debug( getConsumer(), "%s: waiting for op thread\n", __FUNCTION__ );
173 pthread_join(m_op_thread, NULL);
174 mlt_log_debug( getConsumer(), "%s: finished op thread\n", __FUNCTION__ );
175
176 pthread_mutex_destroy( &m_aqueue_lock );
177 pthread_mutex_destroy(&m_op_lock);
178 pthread_mutex_destroy(&m_op_arg_mutex);
179 pthread_cond_destroy(&m_op_arg_cond);
180
181 mlt_log_debug( getConsumer(), "%s: exiting\n", __FUNCTION__ );
182 }
183
op(int op_id,int arg)184 int op(int op_id, int arg)
185 {
186 int r;
187
188 // lock operation mutex
189 pthread_mutex_lock(&m_op_lock);
190
191 mlt_log_debug( getConsumer(), "%s: op_id=%d\n", __FUNCTION__, op_id );
192
193 // notify op id
194 pthread_mutex_lock(&m_op_arg_mutex);
195 m_op_id = op_id;
196 m_op_arg = arg;
197 pthread_cond_signal(&m_op_arg_cond);
198 pthread_mutex_unlock(&m_op_arg_mutex);
199
200 // wait op done
201 pthread_mutex_lock(&m_op_arg_mutex);
202 while(OP_NONE != m_op_id)
203 pthread_cond_wait(&m_op_arg_cond, &m_op_arg_mutex);
204 pthread_mutex_unlock(&m_op_arg_mutex);
205
206 // save result
207 r = m_op_res;
208
209 mlt_log_debug( getConsumer(), "%s: r=%d\n", __FUNCTION__, r );
210
211 // unlock operation mutex
212 pthread_mutex_unlock(&m_op_lock);
213
214 return r;
215 }
216
217 protected:
218
op_main(void * thisptr)219 static void* op_main(void* thisptr)
220 {
221 DeckLinkConsumer* d = static_cast<DeckLinkConsumer*>(thisptr);
222
223 mlt_log_debug( d->getConsumer(), "%s: entering\n", __FUNCTION__ );
224
225 for (;;)
226 {
227 int o, r = 0;
228
229 // wait op command
230 pthread_mutex_lock ( &d->m_op_arg_mutex );
231 while ( OP_NONE == d->m_op_id )
232 pthread_cond_wait( &d->m_op_arg_cond, &d->m_op_arg_mutex );
233 pthread_mutex_unlock( &d->m_op_arg_mutex );
234 o = d->m_op_id;
235
236 mlt_log_debug( d->getConsumer(), "%s:%d d->m_op_id=%d\n", __FUNCTION__, __LINE__, d->m_op_id );
237
238 switch ( d->m_op_id )
239 {
240 case OP_OPEN:
241 r = d->m_op_res = d->open( d->m_op_arg );
242 break;
243
244 case OP_START:
245 r = d->m_op_res = d->start( d->m_op_arg );
246 break;
247
248 case OP_STOP:
249 r = d->m_op_res = d->stop();
250 break;
251 };
252
253 // notify op done
254 pthread_mutex_lock( &d->m_op_arg_mutex );
255 d->m_op_id = OP_NONE;
256 pthread_cond_signal( &d->m_op_arg_cond );
257 pthread_mutex_unlock( &d->m_op_arg_mutex );
258
259 // post for async
260 if ( OP_START == o && r )
261 d->preroll();
262
263 if ( OP_EXIT == o )
264 {
265 mlt_log_debug( d->getConsumer(), "%s: exiting\n", __FUNCTION__ );
266 return NULL;
267 }
268 };
269
270 return NULL;
271 }
272
open(unsigned card=0)273 bool open( unsigned card = 0 )
274 {
275 unsigned i = 0;
276 #ifdef _WIN32
277 IDeckLinkIterator* deckLinkIterator = NULL;
278 HRESULT result = CoInitialize( NULL );
279 if ( FAILED( result ) )
280 {
281 mlt_log_error( getConsumer(), "COM initialization failed\n" );
282 return false;
283 }
284 result = CoCreateInstance( CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**) &deckLinkIterator );
285 if ( FAILED( result ) )
286 {
287 mlt_log_warning( getConsumer(), "The DeckLink drivers not installed.\n" );
288 return false;
289 }
290 #else
291 IDeckLinkIterator* deckLinkIterator = CreateDeckLinkIteratorInstance();
292
293 if ( !deckLinkIterator )
294 {
295 mlt_log_warning( getConsumer(), "The DeckLink drivers not installed.\n" );
296 return false;
297 }
298 #endif
299
300 // Connect to the Nth DeckLink instance
301 for ( i = 0; deckLinkIterator->Next( &m_deckLink ) == S_OK ; i++)
302 {
303 if( i == card )
304 break;
305 else
306 SAFE_RELEASE( m_deckLink );
307 }
308 SAFE_RELEASE( deckLinkIterator );
309 if ( !m_deckLink )
310 {
311 mlt_log_error( getConsumer(), "DeckLink card not found\n" );
312 return false;
313 }
314
315 // Obtain the audio/video output interface (IDeckLinkOutput)
316 if ( m_deckLink->QueryInterface( IID_IDeckLinkOutput, (void**)&m_deckLinkOutput ) != S_OK )
317 {
318 mlt_log_error( getConsumer(), "No DeckLink cards support output\n" );
319 SAFE_RELEASE( m_deckLink );
320 return false;
321 }
322
323 // Get the keyer interface
324 IDeckLinkAttributes *deckLinkAttributes = 0;
325 if ( m_deckLink->QueryInterface( IID_IDeckLinkAttributes, (void**) &deckLinkAttributes ) == S_OK )
326 {
327 #ifdef _WIN32
328 BOOL flag = FALSE;
329 #else
330 bool flag = false;
331 #endif
332 if ( deckLinkAttributes->GetFlag( BMDDeckLinkSupportsInternalKeying, &flag ) == S_OK && flag )
333 {
334 if ( m_deckLink->QueryInterface( IID_IDeckLinkKeyer, (void**) &m_deckLinkKeyer ) != S_OK )
335 {
336 mlt_log_error( getConsumer(), "Failed to get keyer\n" );
337 SAFE_RELEASE( m_deckLinkOutput );
338 SAFE_RELEASE( m_deckLink );
339 return false;
340 }
341 }
342 SAFE_RELEASE( deckLinkAttributes );
343 }
344
345 // Provide this class as a delegate to the audio and video output interfaces
346 m_deckLinkOutput->SetScheduledFrameCompletionCallback( this );
347 m_deckLinkOutput->SetAudioCallback( this );
348
349 return true;
350 }
351
preroll()352 int preroll()
353 {
354 mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
355
356 mlt_log_debug( getConsumer(), "%s: starting\n", __FUNCTION__ );
357
358 if ( !mlt_properties_get_int( properties, "running" ) )
359 return 0;
360
361 mlt_log_verbose( getConsumer(), "preroll %u frames\n", m_preroll );
362
363 // preroll frames
364 for ( unsigned i = 0; i < m_preroll ; i++ )
365 ScheduleNextFrame( true );
366
367 // start audio preroll
368 if ( m_isAudio )
369 m_deckLinkOutput->BeginAudioPreroll( );
370 else
371 m_deckLinkOutput->StartScheduledPlayback( 0, m_timescale, 1.0 );
372
373 mlt_log_debug( getConsumer(), "%s: exiting\n", __FUNCTION__ );
374
375 return 0;
376 }
377
start(unsigned preroll)378 bool start( unsigned preroll )
379 {
380 mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
381
382 // Initialize members
383 m_count = 0;
384 m_buffer = NULL;
385 preroll = preroll < PREROLL_MINIMUM ? PREROLL_MINIMUM : preroll;
386 m_inChannels = mlt_properties_get_int( properties, "channels" );
387 if( m_inChannels <= 2 )
388 {
389 m_outChannels = 2;
390 }
391 else if( m_inChannels <= 8 )
392 {
393 m_outChannels = 8;
394 }
395 else
396 {
397 m_outChannels = 16;
398 }
399 m_isAudio = !mlt_properties_get_int( properties, "audio_off" );
400 m_terminate_on_pause = mlt_properties_get_int( properties, "terminate_on_pause" );
401
402 m_displayMode = getDisplayMode();
403 if ( !m_displayMode )
404 {
405 mlt_log_error( getConsumer(), "Profile is not compatible with decklink.\n" );
406 return false;
407 }
408 mlt_properties_set_int( properties, "top_field_first", m_displayMode->GetFieldDominance() == bmdUpperFieldFirst );
409
410 // Set the keyer
411 if ( m_deckLinkKeyer && ( m_isKeyer = mlt_properties_get_int( properties, "keyer" ) ) )
412 {
413 bool external = ( m_isKeyer == 2 );
414 double level = mlt_properties_get_double( properties, "keyer_level" );
415
416 if ( m_deckLinkKeyer->Enable( external ) != S_OK )
417 mlt_log_error( getConsumer(), "Failed to enable %s keyer\n",
418 external ? "external" : "internal" );
419 m_deckLinkKeyer->SetLevel( level <= 1 ? ( level > 0 ? 255 * level : 255 ) : 255 );
420 }
421 else if ( m_deckLinkKeyer )
422 {
423 m_deckLinkKeyer->Disable();
424 }
425
426 // Set the video output mode
427 if ( S_OK != m_deckLinkOutput->EnableVideoOutput( m_displayMode->GetDisplayMode(),
428 (BMDVideoOutputFlags) (bmdVideoOutputFlagDefault | bmdVideoOutputRP188 | bmdVideoOutputVITC) ) )
429 {
430 mlt_log_error( getConsumer(), "Failed to enable video output\n" );
431 return false;
432 }
433
434 // Set the audio output mode
435 if ( m_isAudio && S_OK != m_deckLinkOutput->EnableAudioOutput( bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger,
436 m_outChannels, bmdAudioOutputStreamTimestamped ) )
437 {
438 mlt_log_error( getConsumer(), "Failed to enable audio output\n" );
439 stop();
440 return false;
441 }
442
443 m_preroll = preroll;
444 m_reprio = 2;
445
446 for ( unsigned i = 0; i < ( m_preroll + 2 ) ; i++)
447 {
448 IDeckLinkMutableVideoFrame* frame;
449
450 // Generate a DeckLink video frame
451 if ( S_OK != m_deckLinkOutput->CreateVideoFrame( m_width, m_height,
452 m_width * ( m_isKeyer? 4 : 2 ), m_isKeyer? bmdFormat8BitARGB : bmdFormat8BitYUV, bmdFrameFlagDefault, &frame ) )
453 {
454 mlt_log_error( getConsumer(), "%s: CreateVideoFrame (%d) failed\n", __FUNCTION__, i );
455 return false;
456 }
457
458 mlt_deque_push_back( m_frames, frame );
459 }
460
461 // Set the running state
462 mlt_properties_set_int( properties, "running", 1 );
463
464 return true;
465 }
466
stop()467 bool stop()
468 {
469 mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
470
471 mlt_log_debug( getConsumer(), "%s: starting\n", __FUNCTION__ );
472
473 // Stop the audio and video output streams immediately
474 if ( m_deckLinkOutput )
475 {
476 m_deckLinkOutput->StopScheduledPlayback( 0, 0, 0 );
477 m_deckLinkOutput->DisableAudioOutput();
478 m_deckLinkOutput->DisableVideoOutput();
479 }
480
481 pthread_mutex_lock( &m_aqueue_lock );
482 while ( mlt_frame frame = (mlt_frame) mlt_deque_pop_back( m_aqueue ) )
483 mlt_frame_close( frame );
484 pthread_mutex_unlock( &m_aqueue_lock );
485
486 m_buffer = NULL;
487 while ( IDeckLinkMutableVideoFrame* frame = (IDeckLinkMutableVideoFrame*) mlt_deque_pop_back( m_frames ) )
488 SAFE_RELEASE( frame );
489
490 // set running state is 0
491 mlt_properties_set_int( properties, "running", 0 );
492
493 mlt_consumer_stopped( getConsumer() );
494
495 mlt_log_debug( getConsumer(), "%s: exiting\n", __FUNCTION__ );
496
497 return true;
498 }
499
renderAudio(mlt_frame frame)500 void renderAudio( mlt_frame frame )
501 {
502 mlt_properties properties;
503 properties = MLT_FRAME_PROPERTIES( frame );
504 mlt_properties_set_int64( properties, "m_count", m_count);
505 mlt_properties_inc_ref( properties );
506 pthread_mutex_lock( &m_aqueue_lock );
507 mlt_deque_push_back( m_aqueue, frame );
508 mlt_log_debug( getConsumer(), "%s:%d frame=%p, len=%d\n", __FUNCTION__, __LINE__, frame, mlt_deque_count( m_aqueue ));
509 pthread_mutex_unlock( &m_aqueue_lock );
510 }
511
renderVideo(mlt_frame frame)512 void renderVideo( mlt_frame frame )
513 {
514 HRESULT hr;
515 mlt_image_format format = m_isKeyer? mlt_image_rgb24a : mlt_image_yuv422;
516 uint8_t* image = 0;
517 int rendered = mlt_properties_get_int( MLT_FRAME_PROPERTIES(frame), "rendered");
518 mlt_properties consumer_properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
519 int stride = m_width * ( m_isKeyer? 4 : 2 );
520 int height = m_height;
521 IDeckLinkMutableVideoFrame* decklinkFrame =
522 static_cast<IDeckLinkMutableVideoFrame*>( mlt_deque_pop_front( m_frames ) );
523
524 mlt_log_debug( getConsumer(), "%s: entering\n", __FUNCTION__ );
525
526 m_sliced_swab = mlt_properties_get_int( consumer_properties, "sliced_swab" );
527
528 if ( rendered && !mlt_frame_get_image( frame, &image, &format, &m_width, &height, 0 ) )
529 {
530 if ( decklinkFrame )
531 decklinkFrame->GetBytes( (void**) &m_buffer );
532
533 if ( m_buffer )
534 {
535 // NTSC SDI is always 486 lines
536 if ( m_height == 486 && height == 480 )
537 {
538 // blank first 6 lines
539 if ( m_isKeyer )
540 {
541 memset( m_buffer, 0, stride * 6 );
542 m_buffer += stride * 6;
543 }
544 else for ( int i = 0; i < m_width * 6; i++ )
545 {
546 *m_buffer++ = 128;
547 *m_buffer++ = 16;
548 }
549 }
550 if ( !m_isKeyer )
551 {
552 unsigned char *arg[3] = { image, m_buffer };
553 ssize_t size = stride * height;
554
555 // Normal non-keyer playout - needs byte swapping
556 if ( !m_sliced_swab )
557 swab2( arg[0], arg[1], size );
558 else
559 {
560 arg[2] = (unsigned char*)size;
561 mlt_slices_run_fifo( 0, swab_sliced, arg);
562 }
563 }
564 else if ( !mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "test_image" ) )
565 {
566 // Normal keyer output
567 int y = height + 1;
568 uint32_t* s = (uint32_t*) image;
569 uint32_t* d = (uint32_t*) m_buffer;
570
571 // Need to relocate alpha channel RGBA => ARGB
572 while ( --y )
573 {
574 int x = m_width + 1;
575 while ( --x )
576 {
577 *d++ = ( *s << 8 ) | ( *s >> 24 );
578 s++;
579 }
580 }
581 }
582 else
583 {
584 // Keying blank frames - nullify alpha
585 memset( m_buffer, 0, stride * height );
586 }
587 }
588 }
589 else if ( decklinkFrame )
590 {
591 uint8_t* buffer = NULL;
592 decklinkFrame->GetBytes( (void**) &buffer );
593 if ( buffer )
594 memcpy( buffer, m_buffer, stride * height );
595 }
596 if ( decklinkFrame )
597 {
598 char* vitc;
599
600 // set timecode
601 vitc = mlt_properties_get( MLT_FRAME_PROPERTIES( frame ), "meta.attr.vitc.markup" );
602 if( vitc )
603 {
604 int h, m, s, f;
605 if ( 4 == sscanf( vitc, "%d:%d:%d:%d", &h, &m, &s, &f ) )
606 decklinkFrame->SetTimecodeFromComponents(bmdTimecodeVITC,
607 h, m, s, f, bmdTimecodeFlagDefault);
608 }
609
610 // set userbits
611 vitc = mlt_properties_get( MLT_FRAME_PROPERTIES( frame ), "meta.attr.vitc.userbits" );
612 if( vitc )
613 decklinkFrame->SetTimecodeUserBits(bmdTimecodeVITC,
614 mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "meta.attr.vitc.userbits" ));
615
616 hr = m_deckLinkOutput->ScheduleVideoFrame( decklinkFrame, m_count * m_duration, m_duration, m_timescale );
617 if ( S_OK != hr )
618 mlt_log_error( getConsumer(), "%s:%d: ScheduleVideoFrame failed, hr=%.8X \n", __FUNCTION__, __LINE__, unsigned(hr) );
619 else
620 mlt_log_debug( getConsumer(), "%s: ScheduleVideoFrame SUCCESS\n", __FUNCTION__ );
621 }
622 }
623
render(mlt_frame frame)624 HRESULT render( mlt_frame frame )
625 {
626 HRESULT result = S_OK;
627
628 // Get the audio
629 double speed = mlt_properties_get_double( MLT_FRAME_PROPERTIES(frame), "_speed" );
630
631 if ( m_isAudio && speed == 1.0 )
632 renderAudio( frame );
633
634 // Get the video
635 renderVideo( frame );
636 ++m_count;
637
638 return result;
639 }
640
reprio(int target)641 void reprio( int target )
642 {
643 int r;
644 pthread_t thread;
645 pthread_attr_t tattr;
646 struct sched_param param;
647 mlt_properties properties;
648
649 if( m_reprio & target )
650 return;
651
652 m_reprio |= target;
653
654 properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
655
656 if ( !mlt_properties_get( properties, "priority" ) )
657 return;
658
659 pthread_attr_init(&tattr);
660 pthread_attr_setschedpolicy(&tattr, SCHED_FIFO);
661
662 if ( !strcmp( "max", mlt_properties_get( properties, "priority" ) ) )
663 param.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1;
664 else if ( !strcmp( "min", mlt_properties_get( properties, "priority" ) ) )
665 param.sched_priority = sched_get_priority_min(SCHED_FIFO) + 1;
666 else
667 param.sched_priority = mlt_properties_get_int( properties, "priority" );
668
669 pthread_attr_setschedparam(&tattr, ¶m);
670
671 thread = pthread_self();
672
673 r = pthread_setschedparam(thread, SCHED_FIFO, ¶m);
674 if( r )
675 mlt_log_error( getConsumer(),
676 "%s: [%d] pthread_setschedparam returned %d\n", __FUNCTION__, target, r);
677 else
678 mlt_log_verbose( getConsumer(),
679 "%s: [%d] param.sched_priority=%d\n", __FUNCTION__, target, param.sched_priority);
680 }
681
682 // *** DeckLink API implementation of IDeckLinkVideoOutputCallback IDeckLinkAudioOutputCallback *** //
683
684 // IUnknown needs only a dummy implementation
QueryInterface(REFIID iid,LPVOID * ppv)685 virtual HRESULT STDMETHODCALLTYPE QueryInterface( REFIID iid, LPVOID *ppv )
686 { return E_NOINTERFACE; }
AddRef()687 virtual ULONG STDMETHODCALLTYPE AddRef()
688 { return 1; }
Release()689 virtual ULONG STDMETHODCALLTYPE Release()
690 { return 1; }
691
692 /************************* DeckLink API Delegate Methods *****************************/
693
694 #ifdef _WIN32
RenderAudioSamples(BOOL preroll)695 virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples ( BOOL preroll )
696 #else
697 virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples ( bool preroll )
698 #endif
699 {
700 pthread_mutex_lock( &m_aqueue_lock );
701 mlt_log_debug( getConsumer(), "%s: ENTERING preroll=%d, len=%d\n", __FUNCTION__, (int)preroll, mlt_deque_count( m_aqueue ));
702 mlt_frame frame = (mlt_frame) mlt_deque_pop_front( m_aqueue );
703 pthread_mutex_unlock( &m_aqueue_lock );
704
705 reprio( 2 );
706
707 if ( frame )
708 {
709 mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
710 uint64_t m_count = mlt_properties_get_int64( properties, "m_count" );
711 mlt_audio_format format = mlt_audio_s16;
712 int frequency = bmdAudioSampleRate48kHz;
713 int samples = mlt_audio_calculate_frame_samples( m_fps, frequency, m_count );
714 int16_t *pcm = 0;
715
716 if ( !mlt_frame_get_audio( frame, (void**) &pcm, &format, &frequency, &m_inChannels, &samples ) )
717 {
718 HRESULT hr;
719 int16_t* outBuff = NULL;
720 mlt_log_debug( getConsumer(), "%s:%d, samples=%d, channels=%d, freq=%d\n",
721 __FUNCTION__, __LINE__, samples, m_inChannels, frequency );
722
723 if( m_inChannels != m_outChannels )
724 {
725 int s = 0;
726 int c = 0;
727 int size = mlt_audio_format_size( format, samples, m_outChannels );
728 int16_t* src = pcm;
729 int16_t* dst = (int16_t*)mlt_pool_alloc( size );
730 outBuff = dst;
731 for( s = 0; s < samples; s++ )
732 {
733 for( c = 0; c < m_outChannels; c++ )
734 {
735 if( c < m_inChannels )
736 {
737 *dst = *src;
738 src++;
739 }
740 else
741 {
742 // Fill silence if there are more out channels than in channels.
743 *dst = 0;
744 }
745 }
746 for( c = 0; c < m_inChannels - m_outChannels; c++ )
747 {
748 // Drop samples if there are more in channels than out channels.
749 src++;
750 }
751 }
752 pcm = outBuff;
753 }
754
755 #ifdef _WIN32
756 #define DECKLINK_UNSIGNED_FORMAT "%lu"
757 unsigned long written = 0;
758 #else
759 #define DECKLINK_UNSIGNED_FORMAT "%u"
760 uint32_t written = 0;
761 #endif
762 BMDTimeValue streamTime = m_count * frequency * m_duration / m_timescale;
763 #ifdef _WIN32
764 hr = m_deckLinkOutput->ScheduleAudioSamples( pcm, samples, streamTime, frequency, (unsigned long*) &written );
765 #else
766 hr = m_deckLinkOutput->ScheduleAudioSamples( pcm, samples, streamTime, frequency, &written );
767 #endif
768 if ( S_OK != hr )
769 mlt_log_error( getConsumer(), "%s:%d ScheduleAudioSamples failed, hr=%.8X \n", __FUNCTION__, __LINE__, unsigned(hr) );
770 else
771 mlt_log_debug( getConsumer(), "%s:%d ScheduleAudioSamples success " DECKLINK_UNSIGNED_FORMAT " samples\n", __FUNCTION__, __LINE__, written );
772 if ( written != (uint32_t) samples )
773 mlt_log_verbose( getConsumer(), "renderAudio: samples=%d, written=" DECKLINK_UNSIGNED_FORMAT "\n", samples, written );
774
775 mlt_pool_release( outBuff );
776 }
777 else
778 mlt_log_error( getConsumer(), "%s:%d mlt_frame_get_audio failed\n", __FUNCTION__, __LINE__);
779
780 mlt_frame_close( frame );
781
782 if ( !preroll )
783 RenderAudioSamples ( preroll );
784 }
785
786 if ( preroll )
787 m_deckLinkOutput->StartScheduledPlayback( 0, m_timescale, 1.0 );
788
789 return S_OK;
790 }
791
ScheduledFrameCompleted(IDeckLinkVideoFrame * completedFrame,BMDOutputFrameCompletionResult completed)792 virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted( IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult completed )
793 {
794 mlt_log_debug( getConsumer(), "%s: ENTERING\n", __FUNCTION__ );
795
796 mlt_deque_push_back( m_frames, completedFrame );
797
798 // change priority of video callback thread
799 reprio( 1 );
800
801 // When a video frame has been released by the API, schedule another video frame to be output
802
803 // ignore handler if frame was flushed
804 if ( bmdOutputFrameFlushed == completed )
805 return S_OK;
806
807 // schedule next frame
808 ScheduleNextFrame( false );
809
810 // step forward frames counter if underrun
811 if ( bmdOutputFrameDisplayedLate == completed )
812 {
813 mlt_log_verbose( getConsumer(), "ScheduledFrameCompleted: bmdOutputFrameDisplayedLate == completed\n" );
814 }
815 if ( bmdOutputFrameDropped == completed )
816 {
817 mlt_log_verbose( getConsumer(), "ScheduledFrameCompleted: bmdOutputFrameDropped == completed\n" );
818 m_count++;
819 ScheduleNextFrame( false );
820 }
821
822 return S_OK;
823 }
824
ScheduledPlaybackHasStopped()825 virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
826 {
827 return mlt_consumer_is_stopped( getConsumer() ) ? S_FALSE : S_OK;
828 }
829
ScheduleNextFrame(bool preroll)830 void ScheduleNextFrame( bool preroll )
831 {
832 // get the consumer
833 mlt_consumer consumer = getConsumer();
834
835 // Get the properties
836 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
837
838 // Frame and size
839 mlt_frame frame = NULL;
840
841 mlt_log_debug( getConsumer(), "%s:%d: preroll=%d\n", __FUNCTION__, __LINE__, preroll);
842
843 while ( !frame && (mlt_properties_get_int( properties, "running" ) || preroll ) )
844 {
845 mlt_log_timings_begin();
846 frame = mlt_consumer_rt_frame( consumer );
847 mlt_log_timings_end( NULL, "mlt_consumer_rt_frame" );
848 if ( frame )
849 {
850 mlt_log_timings_begin();
851 render( frame );
852 mlt_log_timings_end( NULL, "render" );
853
854 mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
855
856 // terminate on pause
857 if ( m_terminate_on_pause &&
858 mlt_properties_get_double( MLT_FRAME_PROPERTIES( frame ), "_speed" ) == 0.0 )
859 stop();
860
861 mlt_frame_close( frame );
862 }
863 else
864 mlt_log_warning( getConsumer(), "%s: mlt_consumer_rt_frame return NULL\n", __FUNCTION__ );
865 }
866 }
867
868 };
869
870 /** Start the consumer.
871 */
872
start(mlt_consumer consumer)873 static int start( mlt_consumer consumer )
874 {
875 // Get the properties
876 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
877 DeckLinkConsumer* decklink = (DeckLinkConsumer*) consumer->child;
878 return decklink->op( OP_START, mlt_properties_get_int( properties, "preroll" ) ) ? 0 : 1;
879 }
880
881 /** Stop the consumer.
882 */
883
stop(mlt_consumer consumer)884 static int stop( mlt_consumer consumer )
885 {
886 int r;
887
888 mlt_log_debug( MLT_CONSUMER_SERVICE(consumer), "%s: entering\n", __FUNCTION__ );
889
890 // Get the properties
891 DeckLinkConsumer* decklink = (DeckLinkConsumer*) consumer->child;
892 r = decklink->op(OP_STOP, 0);
893
894 mlt_log_debug( MLT_CONSUMER_SERVICE(consumer), "%s: exiting\n", __FUNCTION__ );
895
896 return r;
897 }
898
899 /** Determine if the consumer is stopped.
900 */
901
is_stopped(mlt_consumer consumer)902 static int is_stopped( mlt_consumer consumer )
903 {
904 // Get the properties
905 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
906 return !mlt_properties_get_int( properties, "running" );
907 }
908
909 /** Close the consumer.
910 */
911
close(mlt_consumer consumer)912 static void close( mlt_consumer consumer )
913 {
914 mlt_log_debug( MLT_CONSUMER_SERVICE(consumer), "%s: entering\n", __FUNCTION__ );
915
916 // Stop the consumer
917 mlt_consumer_stop( consumer );
918
919 // Close the parent
920 consumer->close = NULL;
921 mlt_consumer_close( consumer );
922
923 // Free the memory
924 delete (DeckLinkConsumer*) consumer->child;
925
926 mlt_log_debug( MLT_CONSUMER_SERVICE(consumer), "%s: exiting\n", __FUNCTION__ );
927 }
928
929 extern "C" {
930
931 // Listen for the list_devices property to be set
on_property_changed(void *,mlt_properties properties,const char * name)932 static void on_property_changed( void*, mlt_properties properties, const char *name )
933 {
934 IDeckLinkIterator* decklinkIterator = NULL;
935 IDeckLink* decklink = NULL;
936 IDeckLinkInput* decklinkOutput = NULL;
937 int i = 0;
938
939 if ( name && !strcmp( name, "list_devices" ) )
940 mlt_event_block( (mlt_event) mlt_properties_get_data( properties, "list-devices-event", NULL ) );
941 else
942 return;
943
944 #ifdef _WIN32
945 if ( FAILED( CoInitialize( NULL ) ) )
946 return;
947 if ( FAILED( CoCreateInstance( CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL, IID_IDeckLinkIterator, (void**) &decklinkIterator ) ) )
948 return;
949 #else
950 if ( !( decklinkIterator = CreateDeckLinkIteratorInstance() ) )
951 return;
952 #endif
953 for ( ; decklinkIterator->Next( &decklink ) == S_OK; i++ )
954 {
955 if ( decklink->QueryInterface( IID_IDeckLinkOutput, (void**) &decklinkOutput ) == S_OK )
956 {
957 DLString name = NULL;
958 if ( decklink->GetModelName( &name ) == S_OK )
959 {
960 char *name_cstr = getCString( name );
961 const char *format = "device.%d";
962 char *key = (char*) calloc( 1, strlen( format ) + 1 );
963
964 sprintf( key, format, i );
965 mlt_properties_set( properties, key, name_cstr );
966 free( key );
967 freeDLString( name );
968 freeCString( name_cstr );
969 }
970 SAFE_RELEASE( decklinkOutput );
971 }
972 SAFE_RELEASE( decklink );
973 }
974 SAFE_RELEASE( decklinkIterator );
975 mlt_properties_set_int( properties, "devices", i );
976 }
977
978 /** Initialise the consumer.
979 */
980
consumer_decklink_init(mlt_profile profile,mlt_service_type type,const char * id,char * arg)981 mlt_consumer consumer_decklink_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
982 {
983 // Allocate the consumer
984 DeckLinkConsumer* decklink = new DeckLinkConsumer();
985 mlt_consumer consumer = NULL;
986
987 // If allocated
988 if ( !mlt_consumer_init( decklink->getConsumer(), decklink, profile ) )
989 {
990 // If initialises without error
991 if ( decklink->op( OP_OPEN, arg? atoi(arg) : 0 ) )
992 {
993 consumer = decklink->getConsumer();
994 mlt_properties properties = MLT_CONSUMER_PROPERTIES( consumer );
995
996 // Setup callbacks
997 consumer->close = close;
998 consumer->start = start;
999 consumer->stop = stop;
1000 consumer->is_stopped = is_stopped;
1001 mlt_properties_set( properties, "deinterlace_method", "onefield" );
1002
1003 mlt_event event = mlt_events_listen( properties, properties, "property-changed", (mlt_listener) on_property_changed );
1004 mlt_properties_set_data( properties, "list-devices-event", event, 0, NULL, NULL );
1005 }
1006 }
1007
1008 // Return consumer
1009 return consumer;
1010 }
1011
1012 extern mlt_producer producer_decklink_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
1013
metadata(mlt_service_type type,const char * id,void * data)1014 static mlt_properties metadata( mlt_service_type type, const char *id, void *data )
1015 {
1016 char file[ PATH_MAX ];
1017 const char *service_type = NULL;
1018 switch ( type )
1019 {
1020 case consumer_type:
1021 service_type = "consumer";
1022 break;
1023 case producer_type:
1024 service_type = "producer";
1025 break;
1026 default:
1027 return NULL;
1028 }
1029 snprintf( file, PATH_MAX, "%s/decklink/%s_%s.yml", mlt_environment( "MLT_DATA" ), service_type, id );
1030 return mlt_properties_parse_yaml( file );
1031 }
1032
1033 MLT_REPOSITORY
1034 {
1035 MLT_REGISTER( consumer_type, "decklink", consumer_decklink_init );
1036 MLT_REGISTER( producer_type, "decklink", producer_decklink_init );
1037 MLT_REGISTER_METADATA( consumer_type, "decklink", metadata, NULL );
1038 MLT_REGISTER_METADATA( producer_type, "decklink", metadata, NULL );
1039 }
1040
1041 } // extern C
1042