1 /*****************************************************************************
2 * avaudiocapture.m: AVFoundation based audio capture module
3 *****************************************************************************
4 * Copyright © 2018 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>
9 * David Fuhrmann <dfuhrmann at videolan dot org>
11 ****************************************************************************
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU Lesser General Public License as published by
14 * the Free Software Foundation; either version 2.1 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public License
23 * along with this program; if not, write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
37 #include <vlc_common.h>
38 #include <vlc_plugin.h>
39 #include <vlc_input.h>
40 #include <vlc_demux.h>
41 #include <vlc_dialog.h>
43 #import <AvailabilityMacros.h>
44 #import <AVFoundation/AVFoundation.h>
45 #import <CoreMedia/CoreMedia.h>
48 #ifndef MAC_OS_X_VERSION_10_14
49 @interface AVCaptureDevice (AVCaptureDeviceAuthorizationSince10_14)
51 + (void)requestAccessForMediaType:(AVMediaType)mediaType completionHandler:(void (^)(BOOL granted))handler API_AVAILABLE(macos(10.14), ios(7.0));
56 /*****************************************************************************
58 *****************************************************************************/
60 typedef struct demux_sys_t
62 CFTypeRef _Nullable session; // AVCaptureSession
63 es_out_id_t *p_es_audio;
68 /*****************************************************************************
70 *****************************************************************************/
71 @interface VLCAVDecompressedAudioOutput : AVCaptureAudioDataOutput<AVCaptureAudioDataOutputSampleBufferDelegate>
79 @implementation VLCAVDecompressedAudioOutput : AVCaptureAudioDataOutput
81 - (id)initWithDemux:(demux_t *)p_demux
83 if (self = [super init])
85 p_avcapture = p_demux;
87 date_Init(&date, 44100, 1);
88 date_Set(&date, VLC_TICK_0);
93 - (void)captureOutput:(AVCaptureOutput *)captureOutput
94 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
95 fromConnection:(AVCaptureConnection *)connection
98 CMItemCount numSamplesInBuffer = CMSampleBufferGetNumSamples(sampleBuffer);
100 size_t neededBufferListSize = 0;
101 // first get needed size for buffer
102 OSStatus retValue = CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBuffer, &neededBufferListSize, nil, 0, nil, nil, kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, nil);
104 if (retValue != noErr) {
105 msg_Err(p_avcapture, "Error getting sample list buffer size: %d", retValue);
109 CMBlockBufferRef blockBuffer;
110 AudioBufferList *audioBufferList = calloc(1, neededBufferListSize);
111 retValue = CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBuffer, nil, audioBufferList, neededBufferListSize, nil, nil, kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, &blockBuffer);
113 if (retValue != noErr) {
114 msg_Err(p_avcapture, "Cannot get samples from buffer: %d", retValue);
118 if (audioBufferList->mNumberBuffers != 1) {
119 msg_Warn(p_avcapture, "This module expects 1 buffer only, got %d", audioBufferList->mNumberBuffers);
123 int64_t totalDataSize = audioBufferList->mBuffers[0].mDataByteSize;
124 block_t *outBlock = block_Alloc(totalDataSize);
128 date_Increment(&date, (uint32_t)numSamplesInBuffer);
130 memcpy(outBlock->p_buffer, audioBufferList->mBuffers[0].mData, totalDataSize);
131 outBlock->i_pts = date_Get(&date);
132 outBlock->i_nb_samples = (unsigned)numSamplesInBuffer;
134 CFRelease(blockBuffer);
136 demux_sys_t *p_sys = p_avcapture->p_sys;
137 es_out_SetPCR(p_avcapture->out, outBlock->i_pts);
138 es_out_Send(p_avcapture->out, p_sys->p_es_audio, outBlock);
145 /*****************************************************************************
147 *****************************************************************************/
148 static int Control(demux_t *p_demux, int i_query, va_list args)
153 /* Special for access_demux */
154 case DEMUX_CAN_PAUSE:
156 case DEMUX_SET_PAUSE_STATE:
157 case DEMUX_CAN_CONTROL_PACE:
158 pb = (bool*)va_arg(args, bool *);
162 case DEMUX_GET_PTS_DELAY:
163 *va_arg(args, vlc_tick_t *) =
164 VLC_TICK_FROM_MS(var_InheritInteger(p_demux, "live-caching"));
173 /*****************************************************************************
175 *****************************************************************************/
176 static int Open(vlc_object_t *p_this)
178 demux_t *p_demux = (demux_t*)p_this;
180 if (p_demux->out == NULL)
184 NSString *currentDeviceId = @"";
185 if (p_demux->psz_location && *p_demux->psz_location)
186 currentDeviceId = [NSString stringWithUTF8String:p_demux->psz_location];
188 msg_Dbg(p_demux, "avcapture uid = %s", currentDeviceId.UTF8String);
190 NSArray *knownAudioDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio];
191 NSInteger numberOfKnownAudioDevices = [knownAudioDevices count];
193 int selectedDevice = 0;
194 for (;selectedDevice < numberOfKnownAudioDevices; selectedDevice++ )
196 AVCaptureDevice *avf_device = [knownAudioDevices objectAtIndex:selectedDevice];
197 msg_Dbg(p_demux, "avcapture %i/%ld %s %s", selectedDevice, (long)numberOfKnownAudioDevices, [[avf_device modelID] UTF8String], [[avf_device uniqueID] UTF8String]);
198 if ([[[avf_device uniqueID] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] isEqualToString:currentDeviceId]) {
203 AVCaptureDevice *device = nil;
204 if (selectedDevice < numberOfKnownAudioDevices) {
205 device = [knownAudioDevices objectAtIndex:selectedDevice];
207 msg_Dbg(p_demux, "Cannot find designated device as %s, falling back to default.", currentDeviceId.UTF8String);
208 device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
212 vlc_dialog_display_error(p_demux, _("No Audio Input device found"),
213 _("Your Mac does not seem to be equipped with a suitable audio input device."
214 "Please check your connectors and drivers."));
215 msg_Err(p_demux, "Can't find any Audio device");
219 if ([device isInUseByAnotherApplication]) {
220 msg_Err(p_demux, "Capture device is exclusively in use by another application");
224 if (@available(macOS 10.14, *)) {
225 msg_Dbg(p_demux, "Check user consent for access to the audio device");
227 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
228 __block bool accessGranted = NO;
229 [AVCaptureDevice requestAccessForMediaType: AVMediaTypeAudio completionHandler:^(BOOL granted) {
230 accessGranted = granted;
231 dispatch_semaphore_signal(sema);
233 dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
234 if (!accessGranted) {
235 msg_Err(p_demux, "Can't use the audio device as access has not been granted by the user");
236 vlc_dialog_display_error(p_demux, _("Problem accessing a system resource"),
237 _("Please open \"System Preferences\" -> \"Security & Privacy\" "
238 "and allow VLC to access your microphone."));
244 NSError *error = nil;
245 AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
247 msg_Err(p_demux, "can't create a valid capture input facility (%ld)", [error code]);
251 /* Now we can init */
252 int audiocodec = VLC_CODEC_FL32;
253 es_format_t audiofmt;
254 es_format_Init(&audiofmt, AUDIO_ES, audiocodec);
256 audiofmt.audio.i_format = audiocodec;
257 audiofmt.audio.i_rate = 44100;
259 * i_physical_channels Describes the channels configuration of the
260 * samples (ie. number of channels which are available in the
261 * buffer, and positions).
263 audiofmt.audio.i_physical_channels = AOUT_CHAN_RIGHT | AOUT_CHAN_LEFT;
265 * Please note that it may be completely arbitrary - buffers are not
266 * obliged to contain a integral number of so-called "frames". It's
267 * just here for the division:
268 * buffer_size = i_nb_samples * i_bytes_per_frame / i_frame_length
270 audiofmt.audio.i_bitspersample = 32;
271 audiofmt.audio.i_channels = 2;
272 audiofmt.audio.i_blockalign = audiofmt.audio.i_channels * (audiofmt.audio.i_bitspersample / 8);
273 audiofmt.i_bitrate = audiofmt.audio.i_channels * audiofmt.audio.i_rate * audiofmt.audio.i_bitspersample;
276 AVCaptureSession *session = [[AVCaptureSession alloc] init];
277 [session addInput:input];
279 VLCAVDecompressedAudioOutput *output = [[VLCAVDecompressedAudioOutput alloc] initWithDemux:p_demux];
280 [session addOutput:output];
282 dispatch_queue_t queue = dispatch_queue_create("avCaptureQueue", NULL);
283 [output setSampleBufferDelegate:output queue:queue];
285 [output setAudioSettings:
287 AVFormatIDKey : @(kAudioFormatLinearPCM),
288 AVLinearPCMBitDepthKey : @(32),
289 AVLinearPCMIsFloatKey : @YES,
290 AVLinearPCMIsBigEndianKey : @NO,
291 AVNumberOfChannelsKey : @(2),
292 AVLinearPCMIsNonInterleaved : @NO,
293 AVSampleRateKey : @(44100.0)
297 p_demux->pf_demux = NULL;
298 p_demux->pf_control = Control;
300 demux_sys_t *p_sys = NULL;
301 p_demux->p_sys = p_sys = calloc(1, sizeof(demux_sys_t));
305 p_sys->session = CFBridgingRetain(session);
306 p_sys->p_es_audio = es_out_Add(p_demux->out, &audiofmt);
308 [session startRunning];
309 msg_Dbg(p_demux, "AVCapture: Audio device ready!");
314 /*****************************************************************************
316 *****************************************************************************/
317 static void Close(vlc_object_t *p_this)
319 demux_t *p_demux = (demux_t*)p_this;
320 demux_sys_t *p_sys = p_demux->p_sys;
323 msg_Dbg(p_demux,"Close AVCapture");
325 ///@todo Investigate why this should be needed
326 // Perform this on main thread, as the framework itself will sometimes try to synchronously
327 // work on main thread. And this will create a dead lock.
328 // [(__bridge AVCaptureSession *)p_sys->session performSelectorOnMainThread:@selector(stopRunning) withObject:nil waitUntilDone:YES];
330 [(__bridge AVCaptureSession *)p_sys->session stopRunning];
331 CFBridgingRelease(p_sys->session);
337 /*****************************************************************************
339 *****************************************************************************/
341 set_shortname(N_("AVFoundation Audio Capture"))
342 set_description(N_("AVFoundation audio capture module."))
343 set_category(CAT_INPUT)
344 set_subcategory(SUBCAT_INPUT_ACCESS)
345 add_shortcut("qtsound")
346 set_capability("access", 0)
347 set_callbacks(Open, Close)