1 /*****************************************************************************
2 * qtsound.m: qtkit (Mac OS X) based audio capture module
3 *****************************************************************************
4 * Copyright © 2011 VLC authors and VideoLAN
6 * Authors: Pierre d'Herbemont <pdherbemont@videolan.org>
7 * Gustaf Neumann <neumann@wu.ac.at>
8 * Michael S. Feurstein <michael.feurstein@wu.ac.at>
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.
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.
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
25 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
39 #include <vlc_demux.h>
40 #include <vlc_dialog.h>
42 #define QTKIT_VERSION_MIN_REQUIRED 70603
44 #import <QTKit/QTKit.h>
46 /*****************************************************************************
48 *****************************************************************************/
49 static int Open(vlc_object_t *p_this);
50 static void Close(vlc_object_t *p_this);
51 static int Demux(demux_t *p_demux);
52 static int Control(demux_t *, int, va_list);
54 /*****************************************************************************
56 *****************************************************************************/
59 set_shortname(N_("QTSound"))
60 set_description(N_("QuickTime Sound Capture"))
61 set_category(CAT_INPUT)
62 set_subcategory(SUBCAT_INPUT_ACCESS)
63 add_shortcut("qtsound")
64 set_capability("access_demux", 0)
65 set_callbacks(Open, Close)
69 /*****************************************************************************
71 *****************************************************************************/
72 @interface VLCDecompressedAudioOutput : QTCaptureDecompressedAudioOutput
75 AudioBuffer *currentAudioBuffer;
76 block_t *rawAudioData;
77 UInt32 numberOfSamples;
82 - (id)initWithDemux:(demux_t *)p_demux;
83 - (void)outputAudioSampleBuffer:(QTSampleBuffer *)sampleBuffer fromConnection:(QTCaptureConnection *)connection;
84 - (BOOL)checkCurrentAudioBuffer;
86 - (mtime_t)getCurrentPts;
87 - (void *)getCurrentAudioBufferData;
88 - (UInt32)getCurrentTotalDataSize;
89 - (UInt32)getNumberOfSamples;
93 @implementation VLCDecompressedAudioOutput : QTCaptureDecompressedAudioOutput
94 - (id)initWithDemux:(demux_t *)p_demux
96 if (self = [super init]) {
98 currentAudioBuffer = nil;
99 date_Init(&date, 44100, 1);
111 - (void)outputAudioSampleBuffer:(QTSampleBuffer *)sampleBuffer fromConnection:(QTCaptureConnection *)connection
113 AudioBufferList *tempAudioBufferList;
114 UInt32 totalDataSize = 0;
117 @synchronized (self) {
118 numberOfSamples = [sampleBuffer numberOfSamples];
119 date_Increment(&date,numberOfSamples);
120 currentPts = date_Get(&date);
122 tempAudioBufferList = [sampleBuffer audioBufferListWithOptions:0];
123 if (tempAudioBufferList->mNumberBuffers == 2) {
125 * Compute totalDataSize as sum of all data blocks in the
128 for (count = 0; count < tempAudioBufferList->mNumberBuffers; count++)
129 totalDataSize += tempAudioBufferList->mBuffers[count].mDataByteSize;
132 * Allocate storage for the interleaved audio data
134 rawAudioData = block_Alloc(totalDataSize * sizeof(float));
135 if (NULL == rawAudioData) {
136 msg_Err(p_qtsound, "Raw audiodata could not be allocated");
140 msg_Err(p_qtsound, "Too many or only one channel found: %i.",
141 tempAudioBufferList->mNumberBuffers);
146 * Interleave raw data (provided in two separate channels as
147 * F32L) with 2 samples per frame
151 const float *b1Ptr, *b2Ptr;
155 uPtr = (float *)rawAudioData,
156 b1Ptr = (const float *) tempAudioBufferList->mBuffers[0].mData,
157 b2Ptr = (const float *) tempAudioBufferList->mBuffers[1].mData;
158 i < numberOfSamples; i++) {
163 if (currentAudioBuffer == nil) {
164 currentAudioBuffer = (AudioBuffer *)malloc(sizeof(AudioBuffer));
165 if (NULL == currentAudioBuffer) {
166 msg_Err(p_qtsound, "AudioBuffer could not be allocated.");
170 currentAudioBuffer->mNumberChannels = 2;
171 currentAudioBuffer->mDataByteSize = totalDataSize;
172 currentAudioBuffer->mData = rawAudioData;
177 - (BOOL)checkCurrentAudioBuffer
179 return (currentAudioBuffer) ? 1 : 0;
184 @synchronized (self) {
186 block_Release(rawAudioData);
190 - (mtime_t)getCurrentPts
192 /* FIXME: can this getter be minimized? */
195 if(!currentAudioBuffer || currentPts == previousPts)
198 @synchronized (self) {
199 pts = previousPts = currentPts;
202 return (currentAudioBuffer->mData) ? currentPts : 0;
205 - (void *)getCurrentAudioBufferData
207 return currentAudioBuffer->mData;
210 - (UInt32)getCurrentTotalDataSize
212 return currentAudioBuffer->mDataByteSize;
215 - (UInt32)getNumberOfSamples
217 return numberOfSamples;
222 /*****************************************************************************
224 *****************************************************************************/
227 QTCaptureSession * session;
228 QTCaptureDevice * audiodevice;
229 VLCDecompressedAudioOutput * audiooutput;
230 es_out_id_t *p_es_audio;
231 int i_audio_max_buffer_size;
234 /*****************************************************************************
235 * Open: initialize interface
236 *****************************************************************************/
237 static int Open(vlc_object_t *p_this)
239 demux_t *p_demux = (demux_t*)p_this;
241 es_format_t audiofmt;
242 char *psz_uid = NULL;
245 NSString *qtk_curraudiodevice_uid;
246 NSAutoreleasePool *pool;
247 NSArray *myAudioDevices, *audioformat_array;
248 QTFormatDescription *audio_format;
249 QTCaptureDeviceInput *audioInput;
250 NSError *o_returnedAudioError;
252 if(p_demux->psz_location && *p_demux->psz_location)
253 psz_uid = p_demux->psz_location;
255 msg_Dbg(p_demux, "qtsound uid = %s", psz_uid);
256 qtk_curraudiodevice_uid = [[NSString alloc] initWithFormat:@"%s", psz_uid];
258 pool = [[NSAutoreleasePool alloc] init];
260 p_demux->p_sys = p_sys = calloc(1, sizeof(demux_sys_t));
264 msg_Dbg(p_demux, "qtsound : uid = %s", [qtk_curraudiodevice_uid UTF8String]);
265 myAudioDevices = [[[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeSound] arrayByAddingObjectsFromArray:[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeMuxed]] retain];
266 if([myAudioDevices count] == 0) {
267 dialog_FatalWait(p_demux, _("No Audio Input device found"),
268 _("Your Mac does not seem to be equipped with a suitable audio input device."
269 "Please check your connectors and drivers."));
270 msg_Err(p_demux, "Can't find any Audio device");
275 for (iaudio = 0; iaudio < [myAudioDevices count]; iaudio++) {
276 QTCaptureDevice *qtk_audioDevice;
277 qtk_audioDevice = [myAudioDevices objectAtIndex:iaudio];
278 msg_Dbg(p_demux, "qtsound audio %u/%lu localizedDisplayName: %s uniqueID: %s", iaudio, [myAudioDevices count], [[qtk_audioDevice localizedDisplayName] UTF8String], [[qtk_audioDevice uniqueID] UTF8String]);
279 if ([[[qtk_audioDevice uniqueID]stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] isEqualToString:qtk_curraudiodevice_uid]) {
280 msg_Dbg(p_demux, "Device found");
286 if(iaudio < [myAudioDevices count])
287 p_sys->audiodevice = [myAudioDevices objectAtIndex:iaudio];
289 /* cannot find designated audio device, fall back to open default audio device */
290 msg_Dbg(p_demux, "Cannot find designated uid audio device as %s. Fall back to open default audio device.", [qtk_curraudiodevice_uid UTF8String]);
291 p_sys->audiodevice = [QTCaptureDevice defaultInputDeviceWithMediaType: QTMediaTypeSound];
293 if(!p_sys->audiodevice) {
294 dialog_FatalWait(p_demux, _("No audio input device found"),
295 _("Your Mac does not seem to be equipped with a suitable audio input device."
296 "Please check your connectors and drivers."));
297 msg_Err(p_demux, "Can't find any Audio device");
302 if(![p_sys->audiodevice open: &o_returnedAudioError]) {
303 msg_Err(p_demux, "Unable to open the audio capture device (%ld)", [o_returnedAudioError code]);
307 if([p_sys->audiodevice isInUseByAnotherApplication] == YES) {
308 msg_Err(p_demux, "default audio capture device is exclusively in use by another application");
311 audioInput = [[QTCaptureDeviceInput alloc] initWithDevice: p_sys->audiodevice];
313 msg_Err(p_demux, "can't create a valid audio capture input facility");
316 msg_Dbg(p_demux, "created valid audio capture input facility");
318 p_sys->audiooutput = [[VLCDecompressedAudioOutput alloc] initWithDemux:p_demux];
319 msg_Dbg (p_demux, "initialized audio output");
321 /* Get the formats */
323 FIXME: the format description gathered here does not seem to be the same
324 in comparison to the format description collected from the actual sampleBuffer.
325 This information needs to be updated some other place. For the time being this shall suffice.
327 The following verbose output is an example of what is read from the input device during the below block
328 [0x3042138] qtsound demux debug: Audio localized format summary: Linear PCM, 24 bit little-endian signed integer, 2 channels, 44100 Hz
329 [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
330 [0x3042138] qtsound demux debug: Flag float 0 bigEndian 0 signedInt 1 packed 0 alignedHigh 0 non interleaved 0 non mixable 0
331 canonical 0 nativeFloatPacked 0 nativeEndian 0
333 However when reading this information from the sampleBuffer during the delegate call from
334 - (void)outputAudioSampleBuffer:(QTSampleBuffer *)sampleBuffer fromConnection:(QTCaptureConnection *)connection;
335 the following data shows up
336 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
337 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
338 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
339 canonical 1 nativeFloatPacked 1 nativeEndian 0
343 little-endian signed integer vs. little-endian floating point
344 format flag 00000004 vs. 00000029
345 bytes per packet 8 vs. 4
347 non interleaved 0 vs. 1 -> this makes a major difference when filling our own buffer
349 nativeFloatPacked 0 vs. 1
351 One would assume we'd need to feed the (es_format_t)audiofmt with the data collected here.
352 This is not the case. Audio will be transmitted in artefacts, due to wrong information.
354 At the moment this data is set manually, however one should consider trying to set this data dynamically
356 audioformat_array = [p_sys->audiodevice formatDescriptions];
358 for(int k = 0; k < [audioformat_array count]; k++) {
359 audio_format = (QTFormatDescription *)[audioformat_array objectAtIndex:k];
361 msg_Dbg(p_demux, "Audio localized format summary: %s", [[audio_format localizedFormatSummary] UTF8String]);
362 msg_Dbg(p_demux, "Audio format description attributes: %s",[[[audio_format formatDescriptionAttributes] description] UTF8String]);
364 AudioStreamBasicDescription asbd = {0};
365 NSValue *asbdValue = [audio_format attributeForKey:QTFormatDescriptionAudioStreamBasicDescriptionAttribute];
366 [asbdValue getValue:&asbd];
368 char formatIDString[5];
369 UInt32 formatID = CFSwapInt32HostToBig (asbd.mFormatID);
370 bcopy (&formatID, formatIDString, 4);
371 formatIDString[4] = '\0';
373 /* kept for development purposes */
375 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",
379 asbd.mBytesPerPacket,
380 asbd.mFramesPerPacket,
382 asbd.mChannelsPerFrame,
383 asbd.mBitsPerChannel);
385 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",
386 (asbd.mFormatFlags & kAudioFormatFlagIsFloat) != 0,
387 (asbd.mFormatFlags & kAudioFormatFlagIsBigEndian) != 0,
388 (asbd.mFormatFlags & kAudioFormatFlagIsSignedInteger) != 0,
389 (asbd.mFormatFlags & kAudioFormatFlagIsPacked) != 0,
390 (asbd.mFormatFlags & kAudioFormatFlagIsAlignedHigh) != 0,
391 (asbd.mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0,
392 (asbd.mFormatFlags & kAudioFormatFlagIsNonMixable) != 0,
394 (asbd.mFormatFlags & kAudioFormatFlagsCanonical) != 0,
395 (asbd.mFormatFlags & kAudioFormatFlagsNativeFloatPacked) != 0,
396 (asbd.mFormatFlags & kAudioFormatFlagsNativeEndian) != 0
401 if([audioformat_array count])
402 audio_format = [audioformat_array objectAtIndex:0];
406 /* Now we can init */
407 audiocodec = VLC_CODEC_FL32;
408 es_format_Init(&audiofmt, AUDIO_ES, audiocodec);
410 audiofmt.audio.i_format = audiocodec;
411 audiofmt.audio.i_rate = 44100;
413 * i_physical_channels Describes the channels configuration of the
414 * samples (ie. number of channels which are available in the
415 * buffer, and positions).
417 audiofmt.audio.i_physical_channels = AOUT_CHAN_RIGHT | AOUT_CHAN_LEFT;
419 * i_original_channels Describes from which original channels,
420 * before downmixing, the buffer is derived.
422 audiofmt.audio.i_original_channels = AOUT_CHAN_RIGHT | AOUT_CHAN_LEFT;
424 * Please note that it may be completely arbitrary - buffers are not
425 * obliged to contain a integral number of so-called "frames". It's
426 * just here for the division:
427 * buffer_size = i_nb_samples * i_bytes_per_frame / i_frame_length
429 audiofmt.audio.i_bitspersample = 32;
430 audiofmt.audio.i_channels = 2;
431 audiofmt.audio.i_blockalign = audiofmt.audio.i_channels * (audiofmt.audio.i_bitspersample / 8);
432 audiofmt.i_bitrate = audiofmt.audio.i_channels * audiofmt.audio.i_rate * audiofmt.audio.i_bitspersample;
433 p_sys->i_audio_max_buffer_size = audiofmt.i_bitrate;
435 p_sys->session = [[QTCaptureSession alloc] init];
437 success = [p_sys->session addInput:audioInput error: &o_returnedAudioError];
439 msg_Err(p_demux, "the audio capture device could not be added to capture session (%ld)", [o_returnedAudioError code]);
443 success = [p_sys->session addOutput:p_sys->audiooutput error: &o_returnedAudioError];
445 msg_Err(p_demux, "audio output could not be added to capture session (%ld)", [o_returnedAudioError code]);
449 [p_sys->session startRunning];
452 p_demux->pf_demux = Demux;
453 p_demux->pf_control = Control;
454 p_demux->info.i_update = 0;
455 p_demux->info.i_title = 0;
456 p_demux->info.i_seekpoint = 0;
458 msg_Dbg(p_demux, "New audio es %d channels %dHz",
459 audiofmt.audio.i_channels, audiofmt.audio.i_rate);
461 p_sys->p_es_audio = es_out_Add(p_demux->out, &audiofmt);
463 [audioInput release];
466 msg_Dbg(p_demux, "QTSound: We have an audio device ready!");
470 [audioInput release];
478 /*****************************************************************************
479 * Close: destroy interface
480 *****************************************************************************/
481 static void Close(vlc_object_t *p_this)
483 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
484 demux_t *p_demux = (demux_t*)p_this;
485 demux_sys_t *p_sys = p_demux->p_sys;
487 [p_sys->session performSelectorOnMainThread:@selector(stopRunning) withObject:nil waitUntilDone:NO];
488 [p_sys->audiooutput performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];
489 [p_sys->session performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];
496 /*****************************************************************************
498 *****************************************************************************/
499 static int Demux(demux_t *p_demux)
501 demux_sys_t *p_sys = p_demux->p_sys;
503 NSAutoreleasePool *pool;
505 p_blocka = block_Alloc(p_sys->i_audio_max_buffer_size);
508 msg_Err(p_demux, "cannot get audio block");
512 @synchronized (p_sys->audiooutput) {
513 if ([p_sys->audiooutput checkCurrentAudioBuffer]) {
514 p_blocka->i_buffer = p_blocka->i_size = [p_sys->audiooutput getCurrentTotalDataSize];
515 p_blocka->p_buffer = p_blocka->p_start = [p_sys->audiooutput getCurrentAudioBufferData];
516 p_blocka->i_nb_samples = [p_sys->audiooutput getNumberOfSamples];
517 p_blocka->i_pts = [p_sys->audiooutput getCurrentPts];
521 if(!p_blocka->i_pts) {
522 // Nothing to transfer yet, just forget
523 block_Release(p_blocka);
529 es_out_Control(p_demux->out, ES_OUT_SET_PCR, p_blocka->i_pts);
530 es_out_Send(p_demux->out, p_sys->p_es_audio, p_blocka);
533 @synchronized (p_sys->audiooutput) {
534 [p_sys->audiooutput freeAudioMem];
540 /*****************************************************************************
542 *****************************************************************************/
543 static int Control(demux_t *p_demux, int i_query, va_list args)
549 /* Special for access_demux */
550 case DEMUX_CAN_PAUSE:
552 case DEMUX_SET_PAUSE_STATE:
553 case DEMUX_CAN_CONTROL_PACE:
554 pb = (bool*)va_arg(args, bool *);
558 case DEMUX_GET_PTS_DELAY:
559 pi64 = (int64_t*)va_arg(args, int64_t *);
560 *pi64 = INT64_C(1000) * var_InheritInteger(p_demux, "live-caching");