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, &param);
670 
671 		thread = pthread_self();
672 
673 		r = pthread_setschedparam(thread, SCHED_FIFO, &param);
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