]> git.sesse.net Git - vlc/blob - modules/access/qtsound.m
Qt4: fix prototype
[vlc] / modules / access / qtsound.m
1 /*****************************************************************************
2 * qtsound.m: qtkit (Mac OS X) based audio capture module
3 *****************************************************************************
4 * Copyright © 2011 VLC authors and VideoLAN
5 *
6 * Authors: Pierre d'Herbemont <pdherbemont@videolan.org>
7 *          Gustaf Neumann <neumann@wu.ac.at>
8 *          Michael S. Feurstein <michael.feurstein@wu.ac.at>
9 *
10 *****************************************************************************
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation; either version 2.1
14 * of the License, or (at your option) any later version.
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
24 *
25 *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
37 #include <vlc_aout.h>
38
39 #include <vlc_demux.h>
40 #include <vlc_dialog.h>
41
42 //#define QTKIT_VERSION_MIN_REQUIRED 70603
43 #define QTKIT_VERSION_MAX_ALLOWED 70700
44
45 #import <QTKit/QTKit.h>
46
47 /*****************************************************************************
48  * Local prototypes.
49  *****************************************************************************/
50 static int Open( vlc_object_t *p_this );
51 static void Close( vlc_object_t *p_this );
52 static int Demux( demux_t *p_demux );
53 static int Control( demux_t *, int, va_list );
54
55 /*****************************************************************************
56  * Module descriptor
57  *****************************************************************************/
58
59 vlc_module_begin()
60 set_shortname( N_("QTSound") )
61 set_description( N_("QuickTime Sound Capture") )
62 set_category( CAT_INPUT )
63 set_subcategory( SUBCAT_INPUT_ACCESS )
64 add_shortcut( "qtsound" )
65 set_capability( "access_demux", 0 )
66 set_callbacks( Open, Close )
67 vlc_module_end ()
68
69
70 /*****************************************************************************
71  * QTKit Bridge
72  *****************************************************************************/
73 @interface VLCDecompressedAudioOutput : QTCaptureDecompressedAudioOutput
74 {
75     demux_t *p_qtsound;
76     AudioBuffer *currentAudioBuffer;
77     block_t *rawAudioData;
78     UInt32 numberOfSamples;
79     date_t date;
80     mtime_t currentPts;
81     mtime_t previousPts;
82 }
83 - (id)initWithDemux:(demux_t *)p_demux;
84 - (void)outputAudioSampleBuffer:(QTSampleBuffer *)sampleBuffer fromConnection:(QTCaptureConnection *)connection;
85 - (BOOL)checkCurrentAudioBuffer;
86 - (void)freeAudioMem;
87 - (mtime_t)getCurrentPts;
88 - (void *)getCurrentAudioBufferData;
89 - (UInt32)getCurrentTotalDataSize;
90 - (UInt32)getNumberOfSamples;
91
92 @end
93
94 @implementation VLCDecompressedAudioOutput : QTCaptureDecompressedAudioOutput
95 - (id)initWithDemux:(demux_t *)p_demux
96 {
97     if( self = [super init] )
98     {
99         p_qtsound = p_demux;
100         currentAudioBuffer = nil;
101         date_Init(&date, 44100, 1);
102         date_Set(&date,0);
103         currentPts = 0;
104         previousPts = 0;
105     }
106     return self;
107 }
108 - (void)dealloc
109 {
110     [super dealloc];
111 }
112
113 - (void)outputAudioSampleBuffer:(QTSampleBuffer *)sampleBuffer fromConnection:(QTCaptureConnection *)connection
114 {
115     AudioBufferList *tempAudioBufferList;
116     UInt32 totalDataSize = 0;
117     UInt32 count = 0;
118
119     @synchronized (self)
120     {
121         numberOfSamples = [sampleBuffer numberOfSamples];
122         date_Increment(&date,numberOfSamples);
123         currentPts = date_Get(&date);
124
125         tempAudioBufferList = [sampleBuffer audioBufferListWithOptions:0];
126         if (tempAudioBufferList->mNumberBuffers == 2)
127         {
128             /*
129              * Compute totalDataSize as sum of all data blocks in the
130              * audio buffer list:
131              */
132             for ( count = 0; count < tempAudioBufferList->mNumberBuffers; count++ )
133             {
134                 totalDataSize += tempAudioBufferList->mBuffers[count].mDataByteSize;
135             }
136             /*
137              * Allocate storage for the interleaved audio data
138              */
139             rawAudioData = block_Alloc(totalDataSize * sizeof(float));
140             if (NULL == rawAudioData)
141             {
142                 msg_Err( p_qtsound, "Raw audiodata could not be allocated" );
143                 return;
144             }
145         }
146         else
147         {
148             msg_Err( p_qtsound, "Too many or only one channel found." );
149             return;
150         }
151
152         /*
153          * Interleave raw data (provided in two separate channels as
154          * F32L) with 2 samples per frame
155          */
156         if ( totalDataSize )
157         {
158             unsigned short i;
159             const float *b1Ptr, *b2Ptr;
160             float *uPtr;
161
162             for (i = 0,
163                  uPtr = (float *)rawAudioData,
164                  b1Ptr = (const float *) tempAudioBufferList->mBuffers[0].mData,
165                  b2Ptr = (const float *) tempAudioBufferList->mBuffers[1].mData;
166                  i < numberOfSamples; i++)
167             {
168                 *uPtr++ = *b1Ptr++;
169                 *uPtr++ = *b2Ptr++;
170             }
171
172             if (currentAudioBuffer == nil)
173             {
174                 currentAudioBuffer = (AudioBuffer *)malloc(sizeof(AudioBuffer));
175                 if (NULL == currentAudioBuffer)
176                 {
177                     msg_Err( p_qtsound, "AudioBuffer could not be allocated." );
178                     return;
179                 }
180             }
181             currentAudioBuffer->mNumberChannels = 2;
182             currentAudioBuffer->mDataByteSize = totalDataSize;
183             currentAudioBuffer->mData = rawAudioData;
184         }
185     }
186 }
187
188 - (BOOL)checkCurrentAudioBuffer
189 {
190     return (currentAudioBuffer) ? 1 : 0;
191 }
192
193 - (void)freeAudioMem
194 {
195     @synchronized (self)
196     {
197         if (rawAudioData) {
198             free(rawAudioData);
199         }
200     }
201 }
202
203 - (mtime_t)getCurrentPts
204 {
205     /* FIXME: can this getter be minimized? */
206     mtime_t pts;
207
208     if( !currentAudioBuffer || currentPts == previousPts )
209     {
210         return 0;
211     }
212
213     @synchronized (self)
214     {
215         pts = previousPts = currentPts;
216     }
217
218     return (currentAudioBuffer->mData) ? currentPts : 0;
219 }
220
221 - (void *)getCurrentAudioBufferData
222 {
223     return currentAudioBuffer->mData;
224 }
225
226 - (UInt32)getCurrentTotalDataSize
227 {
228     return currentAudioBuffer->mDataByteSize;
229 }
230
231 - (UInt32)getNumberOfSamples
232 {
233     return numberOfSamples;
234 }
235
236 @end
237
238 /*****************************************************************************
239  * Struct
240  *****************************************************************************/
241
242 struct demux_sys_t {
243     QTCaptureSession * session;
244     QTCaptureDevice * audiodevice;
245     VLCDecompressedAudioOutput * audiooutput;
246     es_out_id_t *p_es_audio;
247     int i_audio_max_buffer_size;
248 };
249
250 /*****************************************************************************
251  * Open: initialize interface
252  *****************************************************************************/
253 static int Open( vlc_object_t *p_this )
254 {
255     demux_t *p_demux = (demux_t*)p_this;
256     demux_sys_t *p_sys;
257     es_format_t audiofmt;
258     char *psz_uid = NULL;
259     int audiocodec;
260     bool success;
261     NSString *qtk_curraudiodevice_uid;
262     NSAutoreleasePool *pool;
263     NSArray *myAudioDevices, *audioformat_array;
264     QTFormatDescription *audio_format;
265     QTCaptureDeviceInput *audioInput;
266     NSError *o_returnedAudioError;
267
268     if( p_demux->psz_location && *p_demux->psz_location )
269     {
270         psz_uid = p_demux->psz_location;
271     }
272     msg_Dbg( p_demux, "qtsound uid = %s", psz_uid );
273     qtk_curraudiodevice_uid = [[NSString alloc] initWithFormat:@"%s", psz_uid];
274
275     pool = [[NSAutoreleasePool alloc] init];
276
277     p_demux->p_sys = p_sys = calloc( 1, sizeof( demux_sys_t ) );
278     if( !p_sys )
279         return VLC_ENOMEM;
280
281     msg_Dbg( p_demux, "qtsound : uid = %s", [qtk_curraudiodevice_uid UTF8String]);
282     myAudioDevices = [[[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeSound] arrayByAddingObjectsFromArray:[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeMuxed]] retain];
283     if([myAudioDevices count] == 0)
284     {
285         dialog_FatalWait( p_demux, _("No Audio Input device found"),
286                          _("Your Mac does not seem to be equipped with a suitable audio input device."
287                      "Please check your connectors and drivers.") );
288         msg_Err( p_demux, "Can't find any Audio device" );
289
290         goto error;
291     }
292     unsigned iaudio;
293     for(iaudio = 0; iaudio < [myAudioDevices count]; iaudio++){
294         QTCaptureDevice *qtk_audioDevice;
295         qtk_audioDevice = [myAudioDevices objectAtIndex:iaudio];
296         msg_Dbg( p_demux, "qtsound audio %u/%lu localizedDisplayName: %s uniqueID: %s", iaudio, [myAudioDevices count], [[qtk_audioDevice localizedDisplayName] UTF8String], [[qtk_audioDevice uniqueID] UTF8String]);
297         if([[[qtk_audioDevice uniqueID]stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] isEqualToString:qtk_curraudiodevice_uid]){
298             msg_Dbg( p_demux, "Device found" );
299             break;
300         }
301     }
302
303     audioInput = nil;
304     if(iaudio < [myAudioDevices count]){
305         p_sys->audiodevice = [myAudioDevices objectAtIndex:iaudio];
306     }
307     else
308     {
309         /* cannot find designated audio device, fall back to open default audio device */
310         msg_Dbg(p_demux, "Cannot find designated uid audio device as %s. Fall back to open default audio device.", [qtk_curraudiodevice_uid UTF8String]);
311         p_sys->audiodevice = [QTCaptureDevice defaultInputDeviceWithMediaType: QTMediaTypeSound];
312     }
313     if( !p_sys->audiodevice )
314     {
315         dialog_FatalWait( p_demux, _("No audio input device found"),
316                          _("Your Mac does not seem to be equipped with a suitable audio input device."
317                      "Please check your connectors and drivers.") );
318         msg_Err( p_demux, "Can't find any Audio device" );
319
320         goto error;
321     }
322
323     if( ![p_sys->audiodevice open: &o_returnedAudioError] )
324     {
325         msg_Err( p_demux, "Unable to open the audio capture device (%ld)", [o_returnedAudioError code] );
326         goto error;
327     }
328
329     if( [p_sys->audiodevice isInUseByAnotherApplication] == YES )
330     {
331         msg_Err( p_demux, "default audio capture device is exclusively in use by another application" );
332         goto error;
333     }
334     audioInput = [[QTCaptureDeviceInput alloc] initWithDevice: p_sys->audiodevice];
335     if( !audioInput )
336     {
337         msg_Err( p_demux, "can't create a valid audio capture input facility" );
338         goto error;
339     } else {
340         msg_Dbg( p_demux, "created valid audio capture input facility" );
341     }
342
343     p_sys->audiooutput = [[VLCDecompressedAudioOutput alloc] initWithDemux:p_demux];
344     msg_Dbg ( p_demux, "initialized audio output" );
345
346     /* Get the formats */
347     /*
348      FIXME: the format description gathered here does not seem to be the same
349      in comparison to the format description collected from the actual sampleBuffer.
350      This information needs to be updated some other place. For the time being this shall suffice.
351
352      The following verbose output is an example of what is read from the input device during the below block
353      [0x3042138] qtsound demux debug: Audio localized format summary: Linear PCM, 24 bit little-endian signed integer, 2 channels, 44100 Hz
354      [0x3042138] qtsound demux debug: Sample Rate: 44100; Format ID: lpcm; Format Flags: 00000004; Bytes per Packet: 8; Frames per Packet: 1; Bytes per Frame: 8; Channels per Frame: 2; Bits per Channel: 24
355      [0x3042138] qtsound demux debug: Flag float 0 bigEndian 0 signedInt 1 packed 0 alignedHigh 0 non interleaved 0 non mixable 0
356      canonical 0 nativeFloatPacked 0 nativeEndian 0
357
358      However when reading this information from the sampleBuffer during the delegate call from
359      - (void)outputAudioSampleBuffer:(QTSampleBuffer *)sampleBuffer fromConnection:(QTCaptureConnection *)connection;
360      the following data shows up
361      2011-09-23 22:06:03.077 VLC[23070:f103] Audio localized format summary: Linear PCM, 32 bit little-endian floating point, 2 channels, 44100 Hz
362      2011-09-23 22:06:03.078 VLC[23070:f103] Sample Rate: 44100; Format ID: lpcm; Format Flags: 00000029; Bytes per Packet: 4; Frames per Packet: 1; Bytes per Frame: 4; Channels per Frame: 2; Bits per Channel: 32
363      2011-09-23 22:06:03.078 VLC[23070:f103] Flag float 1 bigEndian 0 signedInt 0 packed 1 alignedHigh 0 non interleaved 1 non mixable 0
364      canonical 1 nativeFloatPacked 1 nativeEndian 0
365
366      Note the differences
367      24bit vs. 32bit
368      little-endian signed integer vs. little-endian floating point
369      format flag 00000004 vs. 00000029
370      bytes per packet 8 vs. 4
371      packed 0 vs. 1
372      non interleaved 0 vs. 1 -> this makes a major difference when filling our own buffer
373      canonical 0 vs. 1
374      nativeFloatPacked 0 vs. 1
375
376      One would assume we'd need to feed the (es_format_t)audiofmt with the data collected here.
377      This is not the case. Audio will be transmitted in artefacts, due to wrong information.
378
379      At the moment this data is set manually, however one should consider trying to set this data dynamically
380      */
381     audioformat_array = [p_sys->audiodevice formatDescriptions];
382     audio_format = NULL;
383     for( int k = 0; k < [audioformat_array count]; k++ )
384     {
385         audio_format = (QTFormatDescription *)[audioformat_array objectAtIndex: k];
386
387         msg_Dbg( p_demux, "Audio localized format summary: %s", [[audio_format localizedFormatSummary] UTF8String]);
388         msg_Dbg( p_demux, "Audio format description attributes: %s",[[[audio_format formatDescriptionAttributes] description] UTF8String]);
389
390         AudioStreamBasicDescription asbd = {0};
391         NSValue *asbdValue =  [audio_format attributeForKey:QTFormatDescriptionAudioStreamBasicDescriptionAttribute];
392         [asbdValue getValue:&asbd];
393
394         char formatIDString[5];
395         UInt32 formatID = CFSwapInt32HostToBig (asbd.mFormatID);
396         bcopy (&formatID, formatIDString, 4);
397         formatIDString[4] = '\0';
398
399         /* kept for development purposes */
400 #if 0
401         msg_Dbg( p_demux, "Sample Rate: %.0lf; Format ID: %s; Format Flags: %.8x; Bytes per Packet: %d; Frames per Packet: %d; Bytes per Frame: %d; Channels per Frame: %d; Bits per Channel: %d",
402                 asbd.mSampleRate,
403                 formatIDString,
404                 asbd.mFormatFlags,
405                 asbd.mBytesPerPacket,
406                 asbd.mFramesPerPacket,
407                 asbd.mBytesPerFrame,
408                 asbd.mChannelsPerFrame,
409                 asbd.mBitsPerChannel);
410
411         msg_Dbg( p_demux, "Flag float %d bigEndian %d signedInt %d packed %d alignedHigh %d non interleaved %d non mixable %d\ncanonical %d nativeFloatPacked %d nativeEndian %d",
412                 (asbd.mFormatFlags & kAudioFormatFlagIsFloat) != 0,
413                 (asbd.mFormatFlags & kAudioFormatFlagIsBigEndian) != 0,
414                 (asbd.mFormatFlags & kAudioFormatFlagIsSignedInteger) != 0,
415                 (asbd.mFormatFlags & kAudioFormatFlagIsPacked) != 0,
416                 (asbd.mFormatFlags & kAudioFormatFlagIsAlignedHigh) != 0,
417                 (asbd.mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0,
418                 (asbd.mFormatFlags & kAudioFormatFlagIsNonMixable) != 0,
419
420                 (asbd.mFormatFlags & kAudioFormatFlagsCanonical) != 0,
421                 (asbd.mFormatFlags & kAudioFormatFlagsNativeFloatPacked) != 0,
422                 (asbd.mFormatFlags & kAudioFormatFlagsNativeEndian) != 0
423                 );
424 #endif
425     }
426
427     if( [audioformat_array count] )
428         audio_format = [audioformat_array objectAtIndex: 0];
429     else goto error;
430
431     /* Now we can init */
432     audiocodec = VLC_CODEC_FL32;
433     es_format_Init( &audiofmt, AUDIO_ES, audiocodec);
434
435     audiofmt.audio.i_format = audiocodec;
436     audiofmt.audio.i_rate = 44100;
437     /*
438      * i_physical_channels Describes the channels configuration of the
439      * samples (ie. number of channels which are available in the
440      * buffer, and positions).
441      */
442     audiofmt.audio.i_physical_channels = AOUT_CHAN_RIGHT | AOUT_CHAN_LEFT;
443     /*
444      * i_original_channels Describes from which original channels,
445      * before downmixing, the buffer is derived.
446      */
447     audiofmt.audio.i_original_channels = AOUT_CHAN_RIGHT | AOUT_CHAN_LEFT;
448     /*
449      * Please note that it may be completely arbitrary - buffers are not
450      * obliged to contain a integral number of so-called "frames". It's
451      * just here for the division:
452      * buffer_size = i_nb_samples * i_bytes_per_frame / i_frame_length
453      */
454     audiofmt.audio.i_bitspersample = 32;
455     audiofmt.audio.i_channels = 2;
456     audiofmt.audio.i_blockalign = audiofmt.audio.i_channels * (audiofmt.audio.i_bitspersample / 8);
457     audiofmt.i_bitrate = audiofmt.audio.i_channels * audiofmt.audio.i_rate * audiofmt.audio.i_bitspersample;
458     p_sys->i_audio_max_buffer_size = audiofmt.i_bitrate;
459
460     p_sys->session = [[QTCaptureSession alloc] init];
461
462     success = [p_sys->session addInput:audioInput error: &o_returnedAudioError];
463     if( !success )
464     {
465         msg_Err( p_demux, "the audio capture device could not be added to capture session (%ld)", [o_returnedAudioError code] );
466         goto error;
467     }
468
469     success = [p_sys->session addOutput:p_sys->audiooutput error: &o_returnedAudioError];
470     if( !success )
471     {
472         msg_Err( p_demux, "audio output could not be added to capture session (%ld)", [o_returnedAudioError code] );
473         goto error;
474     }
475
476     [p_sys->session startRunning];
477
478     /* Set up p_demux */
479     p_demux->pf_demux = Demux;
480     p_demux->pf_control = Control;
481     p_demux->info.i_update = 0;
482     p_demux->info.i_title = 0;
483     p_demux->info.i_seekpoint = 0;
484
485     msg_Dbg( p_demux, "New audio es %d channels %dHz",
486             audiofmt.audio.i_channels, audiofmt.audio.i_rate );
487
488     p_sys->p_es_audio = es_out_Add( p_demux->out, &audiofmt );
489
490     [audioInput release];
491     [pool release];
492
493     msg_Dbg( p_demux, "QTSound: We have an audio device ready!" );
494
495     return VLC_SUCCESS;
496 error:
497     [audioInput release];
498     [pool release];
499
500     free( p_sys );
501
502     return VLC_EGENERIC;
503 }
504
505 /*****************************************************************************
506  * Close: destroy interface
507  *****************************************************************************/
508 static void Close( vlc_object_t *p_this )
509 {
510     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
511     demux_t *p_demux = (demux_t*)p_this;
512     demux_sys_t *p_sys = p_demux->p_sys;
513
514     [p_sys->session performSelectorOnMainThread:@selector(stopRunning) withObject:nil waitUntilDone:NO];
515     [p_sys->audiooutput performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];
516     [p_sys->session performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];
517
518     free( p_sys );
519
520     [pool release];
521 }
522
523 /*****************************************************************************
524  * Demux:
525  *****************************************************************************/
526 static int Demux( demux_t *p_demux )
527 {
528     demux_sys_t *p_sys = p_demux->p_sys;
529     block_t *p_blocka;
530     NSAutoreleasePool *pool;
531
532     p_blocka = block_New( p_demux, p_sys->i_audio_max_buffer_size );
533
534     if( !p_blocka )
535     {
536         msg_Err( p_demux, "cannot get audio block" );
537         return 0;
538     }
539
540     @synchronized (p_sys->audiooutput)
541     {
542         if ( [p_sys->audiooutput checkCurrentAudioBuffer] )
543         {
544             p_blocka->i_buffer = p_blocka->i_size = [p_sys->audiooutput getCurrentTotalDataSize];
545             p_blocka->p_buffer = p_blocka->p_start = [p_sys->audiooutput getCurrentAudioBufferData];
546             p_blocka->i_nb_samples = [p_sys->audiooutput getNumberOfSamples];
547             p_blocka->i_pts = [p_sys->audiooutput getCurrentPts];
548         }
549     }
550
551     if( !p_blocka->i_pts )
552     {
553         // Nothing to transfer yet, just forget
554         block_Release( p_blocka );
555         msleep( 10000 );
556         return 1;
557     }
558
559     if( p_blocka )
560     {
561         es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_blocka->i_pts );
562         es_out_Send( p_demux->out, p_sys->p_es_audio, p_blocka );
563     }
564
565     @synchronized (p_sys->audiooutput)
566     {
567         [p_sys->audiooutput freeAudioMem];
568     }
569
570     return 1;
571 }
572
573 /*****************************************************************************
574  * Control:
575  *****************************************************************************/
576 static int Control( demux_t *p_demux, int i_query, va_list args )
577 {
578     bool *pb;
579     int64_t *pi64;
580
581     switch( i_query )
582     {
583             /* Special for access_demux */
584         case DEMUX_CAN_PAUSE:
585         case DEMUX_CAN_SEEK:
586         case DEMUX_SET_PAUSE_STATE:
587         case DEMUX_CAN_CONTROL_PACE:
588             pb = (bool*)va_arg( args, bool * );
589             *pb = false;
590             return VLC_SUCCESS;
591
592         case DEMUX_GET_PTS_DELAY:
593             pi64 = (int64_t*)va_arg( args, int64_t * );
594             *pi64 = INT64_C(1000) * var_InheritInteger( p_demux, "live-caching" );
595             return VLC_SUCCESS;
596
597         default:
598             return VLC_EGENERIC;
599     }
600     return VLC_EGENERIC;
601 }