]> git.sesse.net Git - mlt/blob - src/modules/rtaudio/consumer_rtaudio.cpp
Merge branch 'master' of git://github.com/mltframework/mlt.git
[mlt] / src / modules / rtaudio / consumer_rtaudio.cpp
1 /*
2  * consumer_rtaudio.c -- output through RtAudio audio wrapper
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 <string.h>
23 #include <pthread.h>
24 #include <sys/time.h>
25 #include "RtAudio.h"
26
27 static void consumer_refresh_cb( mlt_consumer sdl, mlt_consumer consumer, char *name );
28 static int  rtaudio_callback( void *outputBuffer, void *inputBuffer,
29         unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData );
30 static void *consumer_thread_proxy( void *arg );
31 static void *video_thread_proxy( void *arg );
32
33 class RtAudioConsumer
34 {
35 public:
36         struct mlt_consumer_s consumer;
37         RtAudio               rt;
38         int                   device_id;
39         mlt_deque             queue;
40         pthread_t             thread;
41         int                   joined;
42         int                   running;
43         uint8_t               audio_buffer[4096 * 10];
44         int                   audio_avail;
45         pthread_mutex_t       audio_mutex;
46         pthread_cond_t        audio_cond;
47         pthread_mutex_t       video_mutex;
48         pthread_cond_t        video_cond;
49         int                   playing;
50         pthread_cond_t        refresh_cond;
51         pthread_mutex_t       refresh_mutex;
52         int                   refresh_count;
53
54         mlt_consumer getConsumer()
55                 { return &consumer; }
56
57         RtAudioConsumer()
58                 : device_id(-1)
59                 , queue(NULL)
60                 , joined(0)
61                 , running(0)
62                 , audio_avail(0)
63                 , playing(0)
64                 , refresh_count(0)
65         {
66                 memset( &consumer, 0, sizeof( consumer ) );
67         }
68
69         ~RtAudioConsumer()
70         {
71                 // Close the queue
72                 mlt_deque_close( queue );
73
74                 // Destroy mutexes
75                 pthread_mutex_destroy( &audio_mutex );
76                 pthread_cond_destroy( &audio_cond );
77                 pthread_mutex_destroy( &video_mutex );
78                 pthread_cond_destroy( &video_cond );
79                 pthread_mutex_destroy( &refresh_mutex );
80                 pthread_cond_destroy( &refresh_cond );
81
82                 if ( rt.isStreamOpen() )
83                         rt.closeStream();
84         }
85
86         bool open( const char* arg )
87         {
88                 if ( rt.getDeviceCount() < 1 )
89                 {
90                         mlt_log_warning( getConsumer(), "no audio devices found\n" );
91                         return false;
92                 }
93
94 #ifndef __LINUX_ALSA__
95                 device_id = rt.getDefaultOutputDevice();
96 #endif
97                 if ( arg && strcmp( arg, "" ) && strcmp( arg, "default" ) )
98                 {
99                         // Get device ID by name
100                         unsigned int n = rt.getDeviceCount();
101                         RtAudio::DeviceInfo info;
102                         unsigned int i;
103
104                         for ( i = 0; i < n; i++ )
105                         {
106                                 info = rt.getDeviceInfo( i );
107                                 mlt_log_verbose( NULL, "RtAudio device %d = %s\n",
108                                         i, info.name.c_str() );
109                                 if ( info.probed && info.name == arg )
110                                 {
111                                         device_id = i;
112                                         break;
113                                 }
114                         }
115                         // Name selection failed, try arg as numeric
116                         if ( i == n )
117                                 device_id = (int) strtol( arg, NULL, 0 );
118                 }
119
120                 // Create the queue
121                 queue = mlt_deque_init( );
122
123                 // get a handle on properties
124                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( &consumer );
125
126                 // Set the default volume
127                 mlt_properties_set_double( properties, "volume", 1.0 );
128
129                 // This is the initialisation of the consumer
130                 pthread_mutex_init( &audio_mutex, NULL );
131                 pthread_cond_init( &audio_cond, NULL);
132                 pthread_mutex_init( &video_mutex, NULL );
133                 pthread_cond_init( &video_cond, NULL);
134
135                 // Default scaler (for now we'll use nearest)
136                 mlt_properties_set( properties, "rescale", "nearest" );
137                 mlt_properties_set( properties, "deinterlace_method", "onefield" );
138
139                 // Default buffer for low latency
140                 mlt_properties_set_int( properties, "buffer", 1 );
141
142                 // Default audio buffer
143                 mlt_properties_set_int( properties, "audio_buffer", 1024 );
144
145                 // Set the resource to the device name arg
146                 mlt_properties_set( properties, "resource", arg );
147
148                 // Ensure we don't join on a non-running object
149                 joined = 1;
150
151                 // Initialize the refresh handler
152                 pthread_cond_init( &refresh_cond, NULL );
153                 pthread_mutex_init( &refresh_mutex, NULL );
154                 mlt_events_listen( properties, this, "property-changed", ( mlt_listener )consumer_refresh_cb );
155
156                 return true;
157         }
158
159         int start()
160         {
161                 if ( !running )
162                 {
163                         stop();
164                         running = 1;
165                         joined = 0;
166                         pthread_create( &thread, NULL, consumer_thread_proxy, this );
167                 }
168
169                 return 0;
170         }
171
172         int stop()
173         {
174                 if ( running && !joined )
175                 {
176                         // Kill the thread and clean up
177                         joined = 1;
178                         running = 0;
179
180                         // Unlatch the consumer thread
181                         pthread_mutex_lock( &refresh_mutex );
182                         pthread_cond_broadcast( &refresh_cond );
183                         pthread_mutex_unlock( &refresh_mutex );
184
185                         // Cleanup the main thread
186                         pthread_join( thread, NULL );
187
188                         // Unlatch the video thread
189                         pthread_mutex_lock( &video_mutex );
190                         pthread_cond_broadcast( &video_cond );
191                         pthread_mutex_unlock( &video_mutex );
192
193                         // Unlatch the audio callback
194                         pthread_mutex_lock( &audio_mutex );
195                         pthread_cond_broadcast( &audio_cond );
196                         pthread_mutex_unlock( &audio_mutex );
197
198                         if ( rt.isStreamOpen() )
199                         try {
200                                 // Stop the stream
201                                 rt.stopStream();
202                         }
203                         catch ( RtError& e ) {
204                                 mlt_log_error( getConsumer(), "%s\n", e.getMessage().c_str() );
205                         }
206                 }
207
208                 return 0;
209         }
210
211         void consumer_thread()
212         {
213                 // Get the properties
214                 mlt_properties consumer_props = MLT_CONSUMER_PROPERTIES( getConsumer() );
215
216                 // Video thread
217                 pthread_t thread;
218
219                 // internal intialization
220                 int init_audio = 1;
221                 int init_video = 1;
222                 mlt_frame frame = NULL;
223                 mlt_properties properties = NULL;
224                 int duration = 0;
225                 int64_t playtime = 0;
226                 struct timespec tm = { 0, 100000 };
227         //      int last_position = -1;
228
229                 pthread_mutex_lock( &refresh_mutex );
230                 refresh_count = 0;
231                 pthread_mutex_unlock( &refresh_mutex );
232
233                 // Loop until told not to
234                 while ( running )
235                 {
236                         // Get a frame from the attached producer
237                         frame = mlt_consumer_rt_frame( getConsumer() );
238
239                         // Ensure that we have a frame
240                         if ( frame )
241                         {
242                                 // Get the frame properties
243                                 properties =  MLT_FRAME_PROPERTIES( frame );
244
245                                 // Get the speed of the frame
246                                 double speed = mlt_properties_get_double( properties, "_speed" );
247
248                                 // Get refresh request for the current frame
249                                 int refresh = mlt_properties_get_int( consumer_props, "refresh" );
250
251                                 // Clear refresh
252                                 mlt_events_block( consumer_props, consumer_props );
253                                 mlt_properties_set_int( consumer_props, "refresh", 0 );
254                                 mlt_events_unblock( consumer_props, consumer_props );
255
256                                 // Play audio
257                                 init_audio = play_audio( frame, init_audio, &duration );
258
259                                 // Determine the start time now
260                                 if ( playing && init_video )
261                                 {
262                                         // Create the video thread
263                                         pthread_create( &thread, NULL, video_thread_proxy, this );
264
265                                         // Video doesn't need to be initialised any more
266                                         init_video = 0;
267                                 }
268
269                                 // Set playtime for this frame
270                                 mlt_properties_set_int( properties, "playtime", playtime );
271
272                                 while ( running && speed != 0 && mlt_deque_count( queue ) > 15 )
273                                         nanosleep( &tm, NULL );
274
275                                 // Push this frame to the back of the queue
276                                 if ( running && speed )
277                                 {
278                                         pthread_mutex_lock( &video_mutex );
279                                         mlt_deque_push_back( queue, frame );
280                                         pthread_cond_broadcast( &video_cond );
281                                         pthread_mutex_unlock( &video_mutex );
282
283                                         // Calculate the next playtime
284                                         playtime += ( duration * 1000 );
285                                 }
286                                 else if ( running )
287                                 {
288                                         pthread_mutex_lock( &refresh_mutex );
289                                         if ( refresh == 0 && refresh_count <= 0 )
290                                         {
291                                                 play_video( frame );
292                                                 pthread_cond_wait( &refresh_cond, &refresh_mutex );
293                                         }
294                                         mlt_frame_close( frame );
295                                         refresh_count --;
296                                         pthread_mutex_unlock( &refresh_mutex );
297                                 }
298                                 else
299                                 {
300                                         mlt_frame_close( frame );
301                                         frame = NULL;
302                                 }
303
304                                 // Optimisation to reduce latency
305                                 if ( frame && speed == 1.0 )
306                                 {
307                                         // TODO: disabled due to misbehavior on parallel-consumer
308         //                              if ( last_position != -1 && last_position + 1 != mlt_frame_get_position( frame ) )
309         //                                      mlt_consumer_purge( consumer );
310         //                              last_position = mlt_frame_get_position( frame );
311                                 }
312                                 else
313                                 {
314                                         mlt_consumer_purge( getConsumer() );
315         //                              last_position = -1;
316                                 }
317                         }
318                 }
319
320                 // Kill the video thread
321                 if ( init_video == 0 )
322                 {
323                         pthread_mutex_lock( &video_mutex );
324                         pthread_cond_broadcast( &video_cond );
325                         pthread_mutex_unlock( &video_mutex );
326                         pthread_join( thread, NULL );
327                 }
328
329                 while( mlt_deque_count( queue ) )
330                         mlt_frame_close( (mlt_frame) mlt_deque_pop_back( queue ) );
331
332                 audio_avail = 0;
333         }
334
335         int callback( int16_t *outbuf, int16_t *inbuf,
336                 unsigned int samples, double streamTime, RtAudioStreamStatus status )
337         {
338                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
339                 double volume = mlt_properties_get_double( properties, "volume" );
340                 int channels = mlt_properties_get_int( properties, "channels" );
341                 int len = mlt_audio_format_size( mlt_audio_s16, samples, channels );
342
343                 pthread_mutex_lock( &audio_mutex );
344
345                 // Block until audio received
346                 while ( running && len > audio_avail )
347                         pthread_cond_wait( &audio_cond, &audio_mutex );
348
349                 if ( audio_avail >= len )
350                 {
351                         // Place in the audio buffer
352                         memcpy( outbuf, audio_buffer, len );
353
354                         // Remove len from the audio available
355                         audio_avail -= len;
356
357                         // Remove the samples
358                         memmove( audio_buffer, audio_buffer + len, audio_avail );
359                 }
360                 else
361                 {
362                         // Just to be safe, wipe the stream first
363                         memset( outbuf, 0, len );
364
365                         // Copy what we have
366                         memcpy( outbuf, audio_buffer, audio_avail );
367
368                         // No audio left
369                         audio_avail = 0;
370                 }
371
372                 if ( volume != 1.0 )
373                 {
374                         int16_t *p = outbuf;
375                         int i = samples * channels + 1;
376                         while ( --i )
377                                 *p++ *= volume;
378                 }
379
380                 // We're definitely playing now
381                 playing = 1;
382
383                 pthread_cond_broadcast( &audio_cond );
384                 pthread_mutex_unlock( &audio_mutex );
385
386                 return 0;
387         }
388
389         int play_audio( mlt_frame frame, int init_audio, int *duration )
390         {
391                 // Get the properties of this consumer
392                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
393                 mlt_audio_format afmt = mlt_audio_s16;
394
395                 // Set the preferred params of the test card signal
396                 int channels = mlt_properties_get_int( properties, "channels" );
397                 int frequency = mlt_properties_get_int( properties, "frequency" );
398                 int scrub = mlt_properties_get_int( properties, "scrub_audio" );
399                 static int counter = 0;
400                 int samples = mlt_sample_calculator( mlt_properties_get_double( properties, "fps" ), frequency, counter++ );
401                 int16_t *pcm;
402
403                 mlt_frame_get_audio( frame, (void**) &pcm, &afmt, &frequency, &channels, &samples );
404                 *duration = ( ( samples * 1000 ) / frequency );
405
406                 if ( mlt_properties_get_int( properties, "audio_off" ) )
407                 {
408                         playing = 1;
409                         return init_audio;
410                 }
411
412                 if ( init_audio == 1 )
413                 {
414                         RtAudio::StreamParameters parameters;
415                         parameters.deviceId = device_id;
416                         parameters.nChannels = channels;
417                         parameters.firstChannel = 0;
418                         RtAudio::StreamOptions options;
419                         unsigned int bufferFrames = mlt_properties_get_int( properties, "audio_buffer" );
420
421                         if ( device_id == -1 )
422                         {
423                                 options.flags = RTAUDIO_ALSA_USE_DEFAULT;
424                                 parameters.deviceId = 0;
425                         }
426                         if ( mlt_properties_get( properties, "resource" ) )
427                                 parameters.deviceName = mlt_properties_get( properties, "resource" );
428
429                         try {
430                                 rt.openStream( &parameters, NULL, RTAUDIO_SINT16,
431                                         frequency, &bufferFrames, &rtaudio_callback, this, &options );
432                                 rt.startStream();
433                                 init_audio = 0;
434                                 playing = 1;
435                         }
436                         catch ( RtError& e ) {
437                                 mlt_log_error( getConsumer(), "%s\n", e.getMessage().c_str() );
438                                 init_audio = 2;
439                         }
440                 }
441
442                 if ( init_audio == 0 )
443                 {
444                         mlt_properties properties = MLT_FRAME_PROPERTIES( frame );
445                         size_t bytes = ( samples * channels * 2 );
446                         pthread_mutex_lock( &audio_mutex );
447                         while ( running && bytes > ( sizeof( audio_buffer) - audio_avail ) )
448                                 pthread_cond_wait( &audio_cond, &audio_mutex );
449                         if ( running )
450                         {
451                                 if ( scrub || mlt_properties_get_double( properties, "_speed" ) == 1 )
452                                         memcpy( &audio_buffer[ audio_avail ], pcm, bytes );
453                                 else
454                                         memset( &audio_buffer[ audio_avail ], 0, bytes );
455                                 audio_avail += bytes;
456                         }
457                         pthread_cond_broadcast( &audio_cond );
458                         pthread_mutex_unlock( &audio_mutex );
459                 }
460
461                 return init_audio;
462         }
463
464         int play_video( mlt_frame frame )
465         {
466                 // Get the properties of this consumer
467                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
468                 if ( running && !mlt_consumer_is_stopped( getConsumer() ) )
469                         mlt_events_fire( properties, "consumer-frame-show", frame, NULL );
470
471                 return 0;
472         }
473
474         void video_thread()
475         {
476                 // Obtain time of thread start
477                 struct timeval now;
478                 int64_t start = 0;
479                 int64_t elapsed = 0;
480                 struct timespec tm;
481                 mlt_frame next = NULL;
482                 mlt_properties properties = MLT_CONSUMER_PROPERTIES( getConsumer() );
483                 double speed = 0;
484
485                 // Get real time flag
486                 int real_time = mlt_properties_get_int( properties, "real_time" );
487
488                 // Get the current time
489                 gettimeofday( &now, NULL );
490
491                 // Determine start time
492                 start = ( int64_t )now.tv_sec * 1000000 + now.tv_usec;
493
494                 while ( running )
495                 {
496                         // Pop the next frame
497                         pthread_mutex_lock( &video_mutex );
498                         next = (mlt_frame) mlt_deque_pop_front( queue );
499                         while ( next == NULL && running )
500                         {
501                                 pthread_cond_wait( &video_cond, &video_mutex );
502                                 next = (mlt_frame) mlt_deque_pop_front( queue );
503                         }
504                         pthread_mutex_unlock( &video_mutex );
505
506                         if ( !running || next == NULL ) break;
507
508                         // Get the properties
509                         properties =  MLT_FRAME_PROPERTIES( next );
510
511                         // Get the speed of the frame
512                         speed = mlt_properties_get_double( properties, "_speed" );
513
514                         // Get the current time
515                         gettimeofday( &now, NULL );
516
517                         // Get the elapsed time
518                         elapsed = ( ( int64_t )now.tv_sec * 1000000 + now.tv_usec ) - start;
519
520                         // See if we have to delay the display of the current frame
521                         if ( mlt_properties_get_int( properties, "rendered" ) == 1 && running )
522                         {
523                                 // Obtain the scheduled playout time
524                                 int64_t scheduled = mlt_properties_get_int( properties, "playtime" );
525
526                                 // Determine the difference between the elapsed time and the scheduled playout time
527                                 int64_t difference = scheduled - elapsed;
528
529                                 // Smooth playback a bit
530                                 if ( real_time && ( difference > 20000 && speed == 1.0 ) )
531                                 {
532                                         tm.tv_sec = difference / 1000000;
533                                         tm.tv_nsec = ( difference % 1000000 ) * 500;
534                                         nanosleep( &tm, NULL );
535                                 }
536
537                                 // Show current frame if not too old
538                                 if ( !real_time || ( difference > -10000 || speed != 1.0 || mlt_deque_count( queue ) < 2 ) )
539                                         play_video( next );
540
541                                 // If the queue is empty, recalculate start to allow build up again
542                                 if ( real_time && ( mlt_deque_count( queue ) == 0 && speed == 1.0 ) )
543                                 {
544                                         gettimeofday( &now, NULL );
545                                         start = ( ( int64_t )now.tv_sec * 1000000 + now.tv_usec ) - scheduled + 20000;
546                                 }
547                         }
548
549                         // This frame can now be closed
550                         mlt_frame_close( next );
551                         next = NULL;
552                 }
553
554                 if ( next != NULL )
555                         mlt_frame_close( next );
556
557                 mlt_consumer_stopped( getConsumer() );
558         }
559
560 };
561
562 static void consumer_refresh_cb( mlt_consumer sdl, mlt_consumer consumer, char *name )
563 {
564         if ( !strcmp( name, "refresh" ) )
565         {
566                 RtAudioConsumer* rtaudio = (RtAudioConsumer*) consumer->child;
567                 pthread_mutex_lock( &rtaudio->refresh_mutex );
568                 rtaudio->refresh_count = rtaudio->refresh_count <= 0 ? 1 : rtaudio->refresh_count + 1;
569                 pthread_cond_broadcast( &rtaudio->refresh_cond );
570                 pthread_mutex_unlock( &rtaudio->refresh_mutex );
571         }
572 }
573
574 static int rtaudio_callback( void *outputBuffer, void *inputBuffer,
575         unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData )
576 {
577         RtAudioConsumer* rtaudio = (RtAudioConsumer*) userData;
578         return rtaudio->callback( (int16_t*) outputBuffer, (int16_t*) inputBuffer, nFrames, streamTime, status );
579 }
580
581 static void *consumer_thread_proxy( void *arg )
582 {
583         RtAudioConsumer* rtaudio = (RtAudioConsumer*) arg;
584         rtaudio->consumer_thread();
585         return NULL;
586 }
587
588 static void *video_thread_proxy( void *arg )
589 {
590         RtAudioConsumer* rtaudio = (RtAudioConsumer*) arg;
591         rtaudio->video_thread();
592         return NULL;
593 }
594
595 /** Start the consumer.
596  */
597
598 static int start( mlt_consumer consumer )
599 {
600         RtAudioConsumer* rtaudio = (RtAudioConsumer*) consumer->child;
601         return rtaudio->start();
602 }
603
604 /** Stop the consumer.
605  */
606
607 static int stop( mlt_consumer consumer )
608 {
609         RtAudioConsumer* rtaudio = (RtAudioConsumer*) consumer->child;
610         return rtaudio->stop();
611 }
612
613 /** Determine if the consumer is stopped.
614  */
615
616 static int is_stopped( mlt_consumer consumer )
617 {
618         RtAudioConsumer* rtaudio = (RtAudioConsumer*) consumer->child;
619         return !rtaudio->running;
620 }
621
622 /** Close the consumer.
623  */
624
625 static void close( mlt_consumer consumer )
626 {
627         // Stop the consumer
628         mlt_consumer_stop( consumer );
629
630         // Close the parent
631         consumer->close = NULL;
632         mlt_consumer_close( consumer );
633
634         // Free the memory
635         delete (RtAudioConsumer*) consumer->child;
636 }
637
638 extern "C" {
639
640 mlt_consumer consumer_rtaudio_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
641 {
642         // Allocate the consumer
643         RtAudioConsumer* rtaudio = new RtAudioConsumer();
644         mlt_consumer consumer = NULL;
645
646         // If allocated
647         if ( rtaudio && !mlt_consumer_init( rtaudio->getConsumer(), rtaudio, profile ) )
648         {
649                 // If initialises without error
650                 if ( rtaudio->open( arg? arg : getenv( "AUDIODEV" ) ) )
651                 {
652                         // Setup callbacks
653                         consumer = rtaudio->getConsumer();
654                         consumer->close = close;
655                         consumer->start = start;
656                         consumer->stop = stop;
657                         consumer->is_stopped = is_stopped;
658                 }
659                 else
660                 {
661                         mlt_consumer_close( rtaudio->getConsumer() );
662                         delete rtaudio;
663                 }
664         }
665
666         // Return consumer
667         return consumer;
668 }
669
670 static mlt_properties metadata( mlt_service_type type, const char *id, void *data )
671 {
672         char file[ PATH_MAX ];
673         const char *service_type = "consumer";
674         snprintf( file, PATH_MAX, "%s/rtaudio/%s_%s.yml", mlt_environment( "MLT_DATA" ), service_type, id );
675         return mlt_properties_parse_yaml( file );
676 }
677
678 MLT_REPOSITORY
679 {
680         MLT_REGISTER( consumer_type, "rtaudio", consumer_rtaudio_init );
681         MLT_REGISTER_METADATA( consumer_type, "rtaudio", metadata, NULL );
682 }
683
684 } // extern C