Thu, 15 Jan 2015 15:59:08 +0100
Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /*
2 * Copyright © 2011 Mozilla Foundation
3 *
4 * This program is made available under an ISC-style license. See the
5 * accompanying file LICENSE for details.
6 */
7 #undef NDEBUG
8 #include <assert.h>
9 #include <pthread.h>
10 #include <stdlib.h>
11 #include <AudioUnit/AudioUnit.h>
12 #include <CoreAudio/AudioHardware.h>
13 #include <CoreAudio/HostTime.h>
14 #include <CoreFoundation/CoreFoundation.h>
15 #include "cubeb/cubeb.h"
16 #include "cubeb-internal.h"
18 #if !defined(kCFCoreFoundationVersionNumber10_7)
19 /* From CoreFoundation CFBase.h */
20 #define kCFCoreFoundationVersionNumber10_7 635.00
21 #endif
23 #define CUBEB_STREAM_MAX 16
24 #define NBUFS 4
26 static struct cubeb_ops const audiounit_ops;
28 struct cubeb {
29 struct cubeb_ops const * ops;
30 pthread_mutex_t mutex;
31 int active_streams;
32 int limit_streams;
33 };
35 struct cubeb_stream {
36 cubeb * context;
37 AudioUnit unit;
38 cubeb_data_callback data_callback;
39 cubeb_state_callback state_callback;
40 void * user_ptr;
41 AudioStreamBasicDescription sample_spec;
42 pthread_mutex_t mutex;
43 uint64_t frames_played;
44 uint64_t frames_queued;
45 int shutdown;
46 int draining;
47 uint64_t current_latency_frames;
48 uint64_t hw_latency_frames;
49 };
51 static int64_t
52 audiotimestamp_to_latency(AudioTimeStamp const * tstamp, cubeb_stream * stream)
53 {
54 if (!(tstamp->mFlags & kAudioTimeStampHostTimeValid)) {
55 return 0;
56 }
58 uint64_t pres = AudioConvertHostTimeToNanos(tstamp->mHostTime);
59 uint64_t now = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
61 return ((pres - now) * stream->sample_spec.mSampleRate) / 1000000000LL;
62 }
64 static OSStatus
65 audiounit_output_callback(void * user_ptr, AudioUnitRenderActionFlags * flags,
66 AudioTimeStamp const * tstamp, UInt32 bus, UInt32 nframes,
67 AudioBufferList * bufs)
68 {
69 cubeb_stream * stm;
70 unsigned char * buf;
71 long got;
72 OSStatus r;
74 assert(bufs->mNumberBuffers == 1);
75 buf = bufs->mBuffers[0].mData;
77 stm = user_ptr;
79 pthread_mutex_lock(&stm->mutex);
81 stm->current_latency_frames = audiotimestamp_to_latency(tstamp, stm);
83 if (stm->draining || stm->shutdown) {
84 pthread_mutex_unlock(&stm->mutex);
85 if (stm->draining) {
86 r = AudioOutputUnitStop(stm->unit);
87 assert(r == 0);
88 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
89 }
90 return noErr;
91 }
93 pthread_mutex_unlock(&stm->mutex);
94 got = stm->data_callback(stm, stm->user_ptr, buf, nframes);
95 pthread_mutex_lock(&stm->mutex);
96 if (got < 0) {
97 /* XXX handle this case. */
98 assert(false);
99 pthread_mutex_unlock(&stm->mutex);
100 return noErr;
101 }
103 if ((UInt32) got < nframes) {
104 size_t got_bytes = got * stm->sample_spec.mBytesPerFrame;
105 size_t rem_bytes = (nframes - got) * stm->sample_spec.mBytesPerFrame;
107 stm->draining = 1;
109 memset(buf + got_bytes, 0, rem_bytes);
110 }
112 stm->frames_played = stm->frames_queued;
113 stm->frames_queued += got;
114 pthread_mutex_unlock(&stm->mutex);
116 return noErr;
117 }
119 /*static*/ int
120 audiounit_init(cubeb ** context, char const * context_name)
121 {
122 cubeb * ctx;
123 int r;
125 *context = NULL;
127 ctx = calloc(1, sizeof(*ctx));
128 assert(ctx);
130 ctx->ops = &audiounit_ops;
132 r = pthread_mutex_init(&ctx->mutex, NULL);
133 assert(r == 0);
135 ctx->active_streams = 0;
137 ctx->limit_streams = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber10_7;
139 *context = ctx;
141 return CUBEB_OK;
142 }
144 static char const *
145 audiounit_get_backend_id(cubeb * ctx)
146 {
147 return "audiounit";
148 }
150 static int
151 audiounit_get_output_device_id(AudioDeviceID * device_id)
152 {
153 UInt32 size;
154 OSStatus r;
155 AudioObjectPropertyAddress output_device_address = {
156 kAudioHardwarePropertyDefaultOutputDevice,
157 kAudioObjectPropertyScopeGlobal,
158 kAudioObjectPropertyElementMaster
159 };
161 size = sizeof(*device_id);
163 r = AudioObjectGetPropertyData(kAudioObjectSystemObject,
164 &output_device_address,
165 0,
166 NULL,
167 &size,
168 device_id);
169 if (r != noErr) {
170 return CUBEB_ERROR;
171 }
173 return CUBEB_OK;
174 }
176 /* Get the acceptable buffer size (in frames) that this device can work with. */
177 static int
178 audiounit_get_acceptable_latency_range(AudioValueRange * latency_range)
179 {
180 UInt32 size;
181 OSStatus r;
182 AudioDeviceID output_device_id;
183 AudioObjectPropertyAddress output_device_buffer_size_range = {
184 kAudioDevicePropertyBufferFrameSizeRange,
185 kAudioDevicePropertyScopeOutput,
186 kAudioObjectPropertyElementMaster
187 };
189 if (audiounit_get_output_device_id(&output_device_id) != CUBEB_OK) {
190 return CUBEB_ERROR;
191 }
193 /* Get the buffer size range this device supports */
194 size = sizeof(*latency_range);
196 r = AudioObjectGetPropertyData(output_device_id,
197 &output_device_buffer_size_range,
198 0,
199 NULL,
200 &size,
201 latency_range);
202 if (r != noErr) {
203 return CUBEB_ERROR;
204 }
206 return CUBEB_OK;
207 }
209 int
210 audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
211 {
212 UInt32 size;
213 OSStatus r;
214 AudioDeviceID output_device_id;
215 AudioStreamBasicDescription stream_format;
216 AudioObjectPropertyAddress stream_format_address = {
217 kAudioDevicePropertyStreamFormat,
218 kAudioDevicePropertyScopeOutput,
219 kAudioObjectPropertyElementMaster
220 };
222 assert(ctx && max_channels);
224 if (audiounit_get_output_device_id(&output_device_id) != CUBEB_OK) {
225 return CUBEB_ERROR;
226 }
228 size = sizeof(stream_format);
230 r = AudioObjectGetPropertyData(output_device_id,
231 &stream_format_address,
232 0,
233 NULL,
234 &size,
235 &stream_format);
236 if (r != noErr) {
237 return CUBEB_ERROR;
238 }
240 *max_channels = stream_format.mChannelsPerFrame;
242 return CUBEB_OK;
243 }
245 static int
246 audiounit_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
247 {
248 AudioValueRange latency_range;
250 if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) {
251 return CUBEB_ERROR;
252 }
254 *latency_ms = (latency_range.mMinimum * 1000 + params.rate - 1) / params.rate;
256 return CUBEB_OK;
257 }
259 static int
260 audiounit_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
261 {
262 UInt32 size;
263 OSStatus r;
264 Float64 fsamplerate;
265 AudioDeviceID output_device_id;
266 AudioObjectPropertyAddress samplerate_address = {
267 kAudioDevicePropertyNominalSampleRate,
268 kAudioObjectPropertyScopeGlobal,
269 kAudioObjectPropertyElementMaster
270 };
272 if (audiounit_get_output_device_id(&output_device_id) != CUBEB_OK) {
273 return CUBEB_ERROR;
274 }
276 size = sizeof(fsamplerate);
277 r = AudioObjectGetPropertyData(output_device_id,
278 &samplerate_address,
279 0,
280 NULL,
281 &size,
282 &fsamplerate);
284 if (r != noErr) {
285 return CUBEB_ERROR;
286 }
288 *rate = (uint32_t)fsamplerate;
290 return CUBEB_OK;
291 }
293 static void
294 audiounit_destroy(cubeb * ctx)
295 {
296 int r;
298 assert(ctx->active_streams == 0);
300 r = pthread_mutex_destroy(&ctx->mutex);
301 assert(r == 0);
303 free(ctx);
304 }
306 static void audiounit_stream_destroy(cubeb_stream * stm);
308 static int
309 audiounit_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
310 cubeb_stream_params stream_params, unsigned int latency,
311 cubeb_data_callback data_callback, cubeb_state_callback state_callback,
312 void * user_ptr)
313 {
314 AudioStreamBasicDescription ss;
315 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
316 ComponentDescription desc;
317 Component comp;
318 #else
319 AudioComponentDescription desc;
320 AudioComponent comp;
321 #endif
322 cubeb_stream * stm;
323 AURenderCallbackStruct input;
324 unsigned int buffer_size, default_buffer_size;
325 OSStatus r;
326 UInt32 size;
327 AudioDeviceID output_device_id;
328 AudioValueRange latency_range;
330 assert(context);
331 *stream = NULL;
333 memset(&ss, 0, sizeof(ss));
334 ss.mFormatFlags = 0;
336 switch (stream_params.format) {
337 case CUBEB_SAMPLE_S16LE:
338 ss.mBitsPerChannel = 16;
339 ss.mFormatFlags |= kAudioFormatFlagIsSignedInteger;
340 break;
341 case CUBEB_SAMPLE_S16BE:
342 ss.mBitsPerChannel = 16;
343 ss.mFormatFlags |= kAudioFormatFlagIsSignedInteger |
344 kAudioFormatFlagIsBigEndian;
345 break;
346 case CUBEB_SAMPLE_FLOAT32LE:
347 ss.mBitsPerChannel = 32;
348 ss.mFormatFlags |= kAudioFormatFlagIsFloat;
349 break;
350 case CUBEB_SAMPLE_FLOAT32BE:
351 ss.mBitsPerChannel = 32;
352 ss.mFormatFlags |= kAudioFormatFlagIsFloat |
353 kAudioFormatFlagIsBigEndian;
354 break;
355 default:
356 return CUBEB_ERROR_INVALID_FORMAT;
357 }
359 ss.mFormatID = kAudioFormatLinearPCM;
360 ss.mFormatFlags |= kLinearPCMFormatFlagIsPacked;
361 ss.mSampleRate = stream_params.rate;
362 ss.mChannelsPerFrame = stream_params.channels;
364 ss.mBytesPerFrame = (ss.mBitsPerChannel / 8) * ss.mChannelsPerFrame;
365 ss.mFramesPerPacket = 1;
366 ss.mBytesPerPacket = ss.mBytesPerFrame * ss.mFramesPerPacket;
368 pthread_mutex_lock(&context->mutex);
369 if (context->limit_streams && context->active_streams >= CUBEB_STREAM_MAX) {
370 pthread_mutex_unlock(&context->mutex);
371 return CUBEB_ERROR;
372 }
373 context->active_streams += 1;
374 pthread_mutex_unlock(&context->mutex);
376 desc.componentType = kAudioUnitType_Output;
377 desc.componentSubType = kAudioUnitSubType_DefaultOutput;
378 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
379 desc.componentFlags = 0;
380 desc.componentFlagsMask = 0;
381 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
382 comp = FindNextComponent(NULL, &desc);
383 #else
384 comp = AudioComponentFindNext(NULL, &desc);
385 #endif
386 assert(comp);
388 stm = calloc(1, sizeof(*stm));
389 assert(stm);
391 stm->context = context;
392 stm->data_callback = data_callback;
393 stm->state_callback = state_callback;
394 stm->user_ptr = user_ptr;
396 stm->sample_spec = ss;
398 r = pthread_mutex_init(&stm->mutex, NULL);
399 assert(r == 0);
401 stm->frames_played = 0;
402 stm->frames_queued = 0;
403 stm->current_latency_frames = 0;
404 stm->hw_latency_frames = UINT64_MAX;
406 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
407 r = OpenAComponent(comp, &stm->unit);
408 #else
409 r = AudioComponentInstanceNew(comp, &stm->unit);
410 #endif
411 if (r != 0) {
412 audiounit_stream_destroy(stm);
413 return CUBEB_ERROR;
414 }
416 input.inputProc = audiounit_output_callback;
417 input.inputProcRefCon = stm;
418 r = AudioUnitSetProperty(stm->unit, kAudioUnitProperty_SetRenderCallback,
419 kAudioUnitScope_Global, 0, &input, sizeof(input));
420 if (r != 0) {
421 audiounit_stream_destroy(stm);
422 return CUBEB_ERROR;
423 }
425 buffer_size = latency / 1000.0 * ss.mSampleRate;
427 /* Get the range of latency this particular device can work with, and clamp
428 * the requested latency to this acceptable range. */
429 if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) {
430 audiounit_stream_destroy(stm);
431 return CUBEB_ERROR;
432 }
434 if (buffer_size < (unsigned int) latency_range.mMinimum) {
435 buffer_size = (unsigned int) latency_range.mMinimum;
436 } else if (buffer_size > (unsigned int) latency_range.mMaximum) {
437 buffer_size = (unsigned int) latency_range.mMaximum;
438 }
440 /**
441 * Get the default buffer size. If our latency request is below the default,
442 * set it. Otherwise, use the default latency.
443 **/
444 size = sizeof(default_buffer_size);
445 r = AudioUnitGetProperty(stm->unit, kAudioDevicePropertyBufferFrameSize,
446 kAudioUnitScope_Output, 0, &default_buffer_size, &size);
448 if (r != 0) {
449 audiounit_stream_destroy(stm);
450 return CUBEB_ERROR;
451 }
453 // Setting the latency doesn't work well for USB headsets (eg. plantronics).
454 // Keep the default latency for now.
455 #if 0
456 if (buffer_size < default_buffer_size) {
457 /* Set the maximum number of frame that the render callback will ask for,
458 * effectively setting the latency of the stream. This is process-wide. */
459 r = AudioUnitSetProperty(stm->unit, kAudioDevicePropertyBufferFrameSize,
460 kAudioUnitScope_Output, 0, &buffer_size, sizeof(buffer_size));
461 if (r != 0) {
462 audiounit_stream_destroy(stm);
463 return CUBEB_ERROR;
464 }
465 }
466 #endif
468 r = AudioUnitSetProperty(stm->unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
469 0, &ss, sizeof(ss));
470 if (r != 0) {
471 audiounit_stream_destroy(stm);
472 return CUBEB_ERROR;
473 }
475 r = AudioUnitInitialize(stm->unit);
476 if (r != 0) {
477 audiounit_stream_destroy(stm);
478 return CUBEB_ERROR;
479 }
481 *stream = stm;
483 return CUBEB_OK;
484 }
486 static void
487 audiounit_stream_destroy(cubeb_stream * stm)
488 {
489 int r;
491 stm->shutdown = 1;
493 if (stm->unit) {
494 AudioOutputUnitStop(stm->unit);
495 AudioUnitUninitialize(stm->unit);
496 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
497 CloseComponent(stm->unit);
498 #else
499 AudioComponentInstanceDispose(stm->unit);
500 #endif
501 }
503 r = pthread_mutex_destroy(&stm->mutex);
504 assert(r == 0);
506 pthread_mutex_lock(&stm->context->mutex);
507 assert(stm->context->active_streams >= 1);
508 stm->context->active_streams -= 1;
509 pthread_mutex_unlock(&stm->context->mutex);
511 free(stm);
512 }
514 static int
515 audiounit_stream_start(cubeb_stream * stm)
516 {
517 OSStatus r;
518 r = AudioOutputUnitStart(stm->unit);
519 assert(r == 0);
520 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
521 return CUBEB_OK;
522 }
524 static int
525 audiounit_stream_stop(cubeb_stream * stm)
526 {
527 OSStatus r;
528 r = AudioOutputUnitStop(stm->unit);
529 assert(r == 0);
530 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
531 return CUBEB_OK;
532 }
534 static int
535 audiounit_stream_get_position(cubeb_stream * stm, uint64_t * position)
536 {
537 pthread_mutex_lock(&stm->mutex);
538 *position = stm->frames_played;
539 pthread_mutex_unlock(&stm->mutex);
540 return CUBEB_OK;
541 }
543 int
544 audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
545 {
546 pthread_mutex_lock(&stm->mutex);
547 if (stm->hw_latency_frames == UINT64_MAX) {
548 UInt32 size;
549 uint32_t device_latency_frames, device_safety_offset;
550 double unit_latency_sec;
551 AudioDeviceID output_device_id;
552 OSStatus r;
553 AudioObjectPropertyAddress latency_address = {
554 kAudioDevicePropertyLatency,
555 kAudioDevicePropertyScopeOutput,
556 kAudioObjectPropertyElementMaster
557 };
558 AudioObjectPropertyAddress safety_offset_address = {
559 kAudioDevicePropertySafetyOffset,
560 kAudioDevicePropertyScopeOutput,
561 kAudioObjectPropertyElementMaster
562 };
564 r = audiounit_get_output_device_id(&output_device_id);
566 if (r != noErr) {
567 pthread_mutex_unlock(&stm->mutex);
568 return CUBEB_ERROR;
569 }
571 size = sizeof(unit_latency_sec);
572 r = AudioUnitGetProperty(stm->unit,
573 kAudioUnitProperty_Latency,
574 kAudioUnitScope_Global,
575 0,
576 &unit_latency_sec,
577 &size);
578 if (r != noErr) {
579 pthread_mutex_unlock(&stm->mutex);
580 return CUBEB_ERROR;
581 }
583 size = sizeof(device_latency_frames);
584 r = AudioObjectGetPropertyData(output_device_id,
585 &latency_address,
586 0,
587 NULL,
588 &size,
589 &device_latency_frames);
590 if (r != noErr) {
591 pthread_mutex_unlock(&stm->mutex);
592 return CUBEB_ERROR;
593 }
595 size = sizeof(device_safety_offset);
596 r = AudioObjectGetPropertyData(output_device_id,
597 &safety_offset_address,
598 0,
599 NULL,
600 &size,
601 &device_safety_offset);
602 if (r != noErr) {
603 pthread_mutex_unlock(&stm->mutex);
604 return CUBEB_ERROR;
605 }
607 /* This part is fixed and depend on the stream parameter and the hardware. */
608 stm->hw_latency_frames =
609 (uint32_t)(unit_latency_sec * stm->sample_spec.mSampleRate)
610 + device_latency_frames
611 + device_safety_offset;
612 }
614 *latency = stm->hw_latency_frames + stm->current_latency_frames;
615 pthread_mutex_unlock(&stm->mutex);
617 return CUBEB_OK;
618 }
620 static struct cubeb_ops const audiounit_ops = {
621 .init = audiounit_init,
622 .get_backend_id = audiounit_get_backend_id,
623 .get_max_channel_count = audiounit_get_max_channel_count,
624 .get_min_latency = audiounit_get_min_latency,
625 .get_preferred_sample_rate = audiounit_get_preferred_sample_rate,
626 .destroy = audiounit_destroy,
627 .stream_init = audiounit_stream_init,
628 .stream_destroy = audiounit_stream_destroy,
629 .stream_start = audiounit_stream_start,
630 .stream_stop = audiounit_stream_stop,
631 .stream_get_position = audiounit_stream_get_position,
632 .stream_get_latency = audiounit_stream_get_latency
633 };