0012-New-AudioUnit-output-module-for-iOS-based-on-the-Mac.patch 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. From 4aecb223442bfed23a73530ec2f9851b8f1ac783 Mon Sep 17 00:00:00 2001
  2. From: =?UTF-8?q?Felix=20Paul=20Ku=CC=88hne?= <fkuehne@videolan.org>
  3. Date: Tue, 5 Mar 2013 00:53:10 +0100
  4. Subject: [PATCH] New AudioUnit output module for iOS based on the Mac
  5. counterpart code
  6. ---
  7. configure.ac | 13 ++
  8. extras/package/ios/build.sh | 1 +
  9. modules/audio_output/Modules.am | 1 +
  10. modules/audio_output/audiounit_ios.c | 416 +++++++++++++++++++++++++++++++++++
  11. 4 files changed, 431 insertions(+)
  12. create mode 100644 modules/audio_output/audiounit_ios.c
  13. diff --git a/configure.ac b/configure.ac
  14. index 752b891..e4bcb92 100644
  15. --- a/configure.ac
  16. +++ b/configure.ac
  17. @@ -3482,6 +3482,19 @@ then
  18. fi
  19. dnl
  20. +dnl iOS CoreAudio plugin
  21. +dnl
  22. +AC_ARG_ENABLE(ios-audio,
  23. + [ --enable-ios-audio Audio module for iOS (default disabled)])
  24. +if test "${enable_ios_audio}" = "yes"
  25. +then
  26. + AC_CHECK_HEADERS(AudioUnit/AudioUnit.h,
  27. + [ VLC_ADD_PLUGIN([audiounit_ios])
  28. + VLC_ADD_LIBS([audiounit_ios],[-Wl,-framework,CoreAudio,-framework,AudioUnit,-framework,AudioToolbox,-framework,CoreServices])
  29. + ], [ AC_MSG_ERROR([cannot find AudioUnit headers]) ])
  30. +fi
  31. +
  32. +dnl
  33. dnl AudioQueue plugin
  34. dnl
  35. AC_ARG_ENABLE(audioqueue,
  36. diff --git a/extras/package/ios/build.sh b/extras/package/ios/build.sh
  37. index 0d4d7c7..77349a9 100755
  38. --- a/extras/package/ios/build.sh
  39. +++ b/extras/package/ios/build.sh
  40. @@ -262,6 +262,7 @@ ${VLCROOT}/configure \
  41. --disable-macosx-eyetv \
  42. --disable-macosx-vlc-app \
  43. --enable-audioqueue \
  44. + --enable-ios-audio \
  45. --enable-ios-vout \
  46. --enable-ios-vout2 \
  47. --disable-shared \
  48. diff --git a/modules/audio_output/Modules.am b/modules/audio_output/Modules.am
  49. index a8c3206..38e9794 100644
  50. --- a/modules/audio_output/Modules.am
  51. +++ b/modules/audio_output/Modules.am
  52. @@ -1,5 +1,6 @@
  53. SOURCES_waveout = waveout.c windows_audio_common.h
  54. SOURCES_auhal = TPCircularBuffer.h TPCircularBuffer.c auhal.c
  55. +SOURCES_audiounit_ios = TPCircularBuffer.h TPCircularBuffer.c audiounit_ios.c
  56. SOURCES_audioqueue = audioqueue.c
  57. libopensles_android_plugin_la_SOURCES = opensles_android.c
  58. diff --git a/modules/audio_output/audiounit_ios.c b/modules/audio_output/audiounit_ios.c
  59. new file mode 100644
  60. index 0000000..e0f9693
  61. --- /dev/null
  62. +++ b/modules/audio_output/audiounit_ios.c
  63. @@ -0,0 +1,416 @@
  64. +/*****************************************************************************
  65. + * audiounit_ios.c: AudioUnit output plugin for iOS
  66. + *****************************************************************************
  67. + * Copyright (C) 2012 - 2013 VLC authors and VideoLAN
  68. + * $Id$
  69. + *
  70. + * Authors: Felix Paul Kühne <fkuehne at videolan dot org>
  71. + *
  72. + * This program is free software; you can redistribute it and/or modify it
  73. + * under the terms of the GNU Lesser General Public License as published by
  74. + * the Free Software Foundation; either version 2.1 of the License, or
  75. + * (at your option) any later version.
  76. + *
  77. + * This program is distributed in the hope that it will be useful,
  78. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  79. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  80. + * GNU Lesser General Public License for more details.
  81. + *
  82. + * You should have received a copy of the GNU Lesser General Public License
  83. + * along with this program; if not, write to the Free Software Foundation,
  84. + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  85. + *****************************************************************************/
  86. +
  87. +#pragma mark includes
  88. +
  89. +#ifdef HAVE_CONFIG_H
  90. +# import "config.h"
  91. +#endif
  92. +
  93. +#import <vlc_common.h>
  94. +#import <vlc_plugin.h>
  95. +#import <vlc_aout.h>
  96. +
  97. +#import <AudioUnit/AudioUnit.h>
  98. +#import <CoreAudio/CoreAudioTypes.h>
  99. +#import <AudioToolbox/AudioToolbox.h>
  100. +#import <mach/mach_time.h>
  101. +
  102. +#import "TPCircularBuffer.h"
  103. +
  104. +#pragma mark -
  105. +#pragma mark private declarations
  106. +
  107. +#define STREAM_FORMAT_MSG(pre, sfm) \
  108. + pre "[%f][%4.4s][%u][%u][%u][%u][%u][%u]", \
  109. + sfm.mSampleRate, (char *)&sfm.mFormatID, \
  110. + (unsigned int)sfm.mFormatFlags, (unsigned int)sfm.mBytesPerPacket, \
  111. + (unsigned int)sfm.mFramesPerPacket, (unsigned int)sfm.mBytesPerFrame, \
  112. + (unsigned int)sfm.mChannelsPerFrame, (unsigned int)sfm.mBitsPerChannel
  113. +
  114. +#define kBufferLength 2048 * 8 * 8 * 4
  115. +
  116. +/*****************************************************************************
  117. + * aout_sys_t: private audio output method descriptor
  118. + *****************************************************************************
  119. + * This structure is part of the audio output thread descriptor.
  120. + * It describes the CoreAudio specific properties of an output thread.
  121. + *****************************************************************************/
  122. +struct aout_sys_t
  123. +{
  124. + uint8_t chans_to_reorder; /* do we need channel reordering */
  125. + uint8_t chan_table[AOUT_CHAN_MAX];
  126. +
  127. + UInt32 i_numberOfChannels;
  128. + TPCircularBuffer circular_buffer; /* circular buffer to swap the audio data */
  129. +
  130. + /* AUHAL specific */
  131. + AudioComponent au_component; /* The AudioComponent we use */
  132. + AudioUnit au_unit; /* The AudioUnit we use */
  133. +
  134. + int i_rate; /* media sample rate */
  135. + mtime_t i_played_length; /* how much did we play already */
  136. + mtime_t i_last_sample_time; /* last sample time played by the AudioUnit */
  137. + mtime_t i_first_time_stamp;
  138. + bool b_got_first_sample;
  139. +
  140. + vlc_mutex_t lock;
  141. +};
  142. +
  143. +#pragma mark -
  144. +#pragma mark local prototypes & module descriptor
  145. +
  146. +static int Open (vlc_object_t *);
  147. +static void Close (vlc_object_t *);
  148. +static int Start (audio_output_t *, audio_sample_format_t *);
  149. +static int StartAnalog (audio_output_t *, audio_sample_format_t *);
  150. +static void Stop (audio_output_t *);
  151. +
  152. +static void Play (audio_output_t *, block_t *);
  153. +static void Pause (audio_output_t *, bool, mtime_t);
  154. +static void Flush (audio_output_t *, bool);
  155. +static int TimeGet (audio_output_t *, mtime_t *);
  156. +static OSStatus RenderCallback (vlc_object_t *, AudioUnitRenderActionFlags *, const AudioTimeStamp *,
  157. + UInt32 , UInt32, AudioBufferList *);
  158. +vlc_module_begin ()
  159. + set_shortname("audiounit_ios")
  160. + set_description(N_("AudioUnit output for iOS"))
  161. + set_capability("audio output", 101)
  162. + set_category(CAT_AUDIO)
  163. + set_subcategory(SUBCAT_AUDIO_AOUT)
  164. + set_callbacks(Open, Close)
  165. +vlc_module_end ()
  166. +
  167. +#pragma mark -
  168. +#pragma mark initialization
  169. +
  170. +static int Open(vlc_object_t *obj)
  171. +{
  172. + audio_output_t *aout = (audio_output_t *)obj;
  173. + aout_sys_t *sys = malloc(sizeof (*sys));
  174. +
  175. + if (unlikely(sys == NULL))
  176. + return VLC_ENOMEM;
  177. +
  178. + vlc_mutex_init(&sys->lock);
  179. +
  180. + aout->sys = sys;
  181. + aout->start = Start;
  182. + aout->stop = Stop;
  183. +
  184. + return VLC_SUCCESS;
  185. +}
  186. +
  187. +static void Close(vlc_object_t *obj)
  188. +{
  189. + audio_output_t *aout = (audio_output_t *)obj;
  190. + aout_sys_t *sys = aout->sys;
  191. +
  192. + vlc_mutex_destroy(&sys->lock);
  193. +
  194. + free(sys);
  195. +}
  196. +
  197. +static int Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
  198. +{
  199. + struct aout_sys_t *p_sys = NULL;
  200. +
  201. + p_sys = p_aout->sys;
  202. + p_sys->au_component = NULL;
  203. + p_sys->au_unit = NULL;
  204. +
  205. + aout_FormatPrint(p_aout, "VLC is looking for:", fmt);
  206. +
  207. + if (StartAnalog(p_aout, fmt)) {
  208. + msg_Dbg(p_aout, "analog AudioUnit output successfully opened");
  209. + p_aout->play = Play;
  210. + p_aout->flush = Flush;
  211. + p_aout->time_get = TimeGet;
  212. + p_aout->pause = Pause;
  213. + return VLC_SUCCESS;
  214. + }
  215. +
  216. + /* If we reach this, this aout has failed */
  217. + msg_Err(p_aout, "opening AudioUnit output failed");
  218. + return VLC_EGENERIC;
  219. +}
  220. +
  221. +/*
  222. + * StartAnalog: open and setup a HAL AudioUnit to do PCM audio output
  223. + */
  224. +static int StartAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt)
  225. +{
  226. + struct aout_sys_t *p_sys = p_aout->sys;
  227. + UInt32 i_param_size = 0;
  228. + AudioComponentDescription desc;
  229. + AURenderCallbackStruct callback;
  230. + p_aout->sys->chans_to_reorder = 0;
  231. + OSStatus status;
  232. +
  233. + /* Lets go find our Component */
  234. + desc.componentType = kAudioUnitType_Output;
  235. + desc.componentSubType = kAudioUnitSubType_RemoteIO;
  236. + desc.componentManufacturer = kAudioUnitManufacturer_Apple;
  237. + desc.componentFlags = 0;
  238. + desc.componentFlagsMask = 0;
  239. +
  240. + p_sys->au_component = AudioComponentFindNext(NULL, &desc);
  241. + if (p_sys->au_component == NULL) {
  242. + msg_Warn(p_aout, "we cannot find our audio component");
  243. + return false;
  244. + }
  245. +
  246. + status = AudioComponentInstanceNew(p_sys->au_component, &p_sys->au_unit);
  247. + if (status != noErr) {
  248. + msg_Warn(p_aout, "we cannot open our audio component (%li)", status);
  249. + return false;
  250. + }
  251. +
  252. + UInt32 flag = 1;
  253. + status = AudioUnitSetProperty(p_sys->au_unit,
  254. + kAudioOutputUnitProperty_EnableIO,
  255. + kAudioUnitScope_Output,
  256. + 0,
  257. + &flag,
  258. + sizeof(flag));
  259. + if (status != noErr)
  260. + msg_Warn(p_aout, "failed to set IO mode (%li)", status);
  261. +
  262. + /* Get the current format */
  263. + AudioStreamBasicDescription streamDescription;
  264. + streamDescription.mSampleRate = fmt->i_rate;
  265. + fmt->i_format = VLC_CODEC_FL32;
  266. + streamDescription.mFormatID = kAudioFormatLinearPCM;
  267. + streamDescription.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; // FL32
  268. + streamDescription.mChannelsPerFrame = 2;
  269. + streamDescription.mFramesPerPacket = 1;
  270. + streamDescription.mBitsPerChannel = 32;
  271. + streamDescription.mBytesPerFrame = streamDescription.mBitsPerChannel * streamDescription.mChannelsPerFrame / 8;
  272. + streamDescription.mBytesPerPacket = streamDescription.mBytesPerFrame * streamDescription.mFramesPerPacket;
  273. + i_param_size = sizeof(streamDescription);
  274. + p_sys->i_rate = fmt->i_rate;
  275. +
  276. + /* Set the desired format */
  277. + i_param_size = sizeof(AudioStreamBasicDescription);
  278. + status = AudioUnitSetProperty(p_sys->au_unit,
  279. + kAudioUnitProperty_StreamFormat,
  280. + kAudioUnitScope_Input,
  281. + 0,
  282. + &streamDescription,
  283. + i_param_size);
  284. + if (status != noErr) {
  285. + msg_Err(p_aout, "failed to set stream format (%li)", status);
  286. + return false;
  287. + }
  288. + msg_Dbg(p_aout, STREAM_FORMAT_MSG("we set the AU format: " , streamDescription));
  289. +
  290. + /* Retrieve actual format */
  291. + status = AudioUnitGetProperty(p_sys->au_unit,
  292. + kAudioUnitProperty_StreamFormat,
  293. + kAudioUnitScope_Input,
  294. + 0,
  295. + &streamDescription,
  296. + &i_param_size);
  297. + if (status != noErr)
  298. + msg_Warn(p_aout, "failed to verify stream format (%li)", status);
  299. + msg_Dbg(p_aout, STREAM_FORMAT_MSG("the actual set AU format is " , streamDescription));
  300. +
  301. + /* Do the last VLC aout setups */
  302. + aout_FormatPrepare(fmt);
  303. +
  304. + /* set the IOproc callback */
  305. + callback.inputProc = (AURenderCallback) RenderCallback;
  306. + callback.inputProcRefCon = p_aout;
  307. +
  308. + status = AudioUnitSetProperty(p_sys->au_unit,
  309. + kAudioUnitProperty_SetRenderCallback,
  310. + kAudioUnitScope_Input,
  311. + 0, &callback, sizeof(callback));
  312. + if (status != noErr) {
  313. + msg_Err(p_aout, "render callback setup failed (%li)", status);
  314. + return false;
  315. + }
  316. +
  317. + /* AU initiliaze */
  318. + status = AudioUnitInitialize(p_sys->au_unit);
  319. + if (status != noErr) {
  320. + msg_Err(p_aout, "failed to init AudioUnit (%li)", status);
  321. + return false;
  322. + }
  323. +
  324. + /* setup circular buffer */
  325. + TPCircularBufferInit(&p_sys->circular_buffer, kBufferLength);
  326. +
  327. + p_sys->b_got_first_sample = false;
  328. + p_sys->i_played_length = 0;
  329. + p_sys->i_last_sample_time = 0;
  330. + p_sys->i_first_time_stamp = 0;
  331. +
  332. + return true;
  333. +}
  334. +
  335. +static void Stop(audio_output_t *p_aout)
  336. +{
  337. + struct aout_sys_t *p_sys = p_aout->sys;
  338. + OSStatus status;
  339. +
  340. + if (p_sys->au_unit) {
  341. + status = AudioOutputUnitStop(p_sys->au_unit);
  342. + if (status != noErr)
  343. + msg_Warn(p_aout, "failed to stop AudioUnit (%li)", status);
  344. +
  345. + status = AudioComponentInstanceDispose(p_sys->au_unit);
  346. + if (status != noErr)
  347. + msg_Warn(p_aout, "failed to dispose Audio Component instance (%li)", status);
  348. + }
  349. +
  350. + p_sys->i_played_length = 0;
  351. + p_sys->i_last_sample_time = 0;
  352. + p_sys->i_first_time_stamp = 0;
  353. +
  354. + /* clean-up circular buffer */
  355. + TPCircularBufferCleanup(&p_sys->circular_buffer);
  356. +}
  357. +
  358. +#pragma mark -
  359. +#pragma mark actual playback
  360. +
  361. +static void Play (audio_output_t * p_aout, block_t * p_block)
  362. +{
  363. + struct aout_sys_t *p_sys = p_aout->sys;
  364. +
  365. + if (p_block->i_nb_samples > 0) {
  366. + if (!p_sys->b_got_first_sample) {
  367. + /* Start the AU */
  368. + OSStatus status = AudioOutputUnitStart(p_sys->au_unit);
  369. + msg_Dbg(p_aout, "audio output unit started: %li", status);
  370. + p_sys->b_got_first_sample = true;
  371. + }
  372. +
  373. + /* Do the channel reordering */
  374. + if (p_sys->chans_to_reorder) {
  375. + aout_ChannelReorder(p_block->p_buffer,
  376. + p_block->i_buffer,
  377. + p_sys->chans_to_reorder,
  378. + p_sys->chan_table,
  379. + VLC_CODEC_FL32);
  380. + }
  381. +
  382. + /* keep track of the played data */
  383. + p_aout->sys->i_played_length += p_block->i_length;
  384. +
  385. + /* move data to buffer */
  386. + TPCircularBufferProduceBytes(&p_sys->circular_buffer, p_block->p_buffer, p_block->i_buffer);
  387. + }
  388. +
  389. + block_Release(p_block);
  390. +}
  391. +
  392. +static void Pause (audio_output_t *p_aout, bool pause, mtime_t date)
  393. +{
  394. + struct aout_sys_t * p_sys = p_aout->sys;
  395. + VLC_UNUSED(date);
  396. +
  397. + if (pause)
  398. + AudioOutputUnitStop(p_sys->au_unit);
  399. + else
  400. + AudioOutputUnitStart(p_sys->au_unit);
  401. +}
  402. +
  403. +static void Flush(audio_output_t *p_aout, bool wait)
  404. +{
  405. + struct aout_sys_t * p_sys = p_aout->sys;
  406. + VLC_UNUSED(wait);
  407. +
  408. + p_sys->b_got_first_sample = false;
  409. +
  410. + /* flush circular buffer */
  411. + AudioOutputUnitStop(p_aout->sys->au_unit);
  412. + TPCircularBufferClear(&p_aout->sys->circular_buffer);
  413. +
  414. + p_sys->i_played_length = 0;
  415. + p_sys->i_last_sample_time = 0;
  416. + p_sys->i_first_time_stamp = 0;
  417. +}
  418. +
  419. +static int TimeGet(audio_output_t *p_aout, mtime_t *delay)
  420. +{
  421. + struct aout_sys_t * p_sys = p_aout->sys;
  422. +
  423. + vlc_mutex_lock(&p_sys->lock);
  424. + mtime_t i_pos = (p_sys->i_last_sample_time - p_sys->i_first_time_stamp) * CLOCK_FREQ / p_sys->i_rate;
  425. + vlc_mutex_unlock(&p_sys->lock);
  426. +
  427. + if (i_pos > 0) {
  428. + *delay = p_aout->sys->i_played_length - i_pos;
  429. + return 0;
  430. + }
  431. + else
  432. + return -1;
  433. +}
  434. +
  435. +/*****************************************************************************
  436. + * RenderCallback: This function is called everytime the AudioUnit wants
  437. + * us to provide some more audio data.
  438. + * Don't print anything during normal playback, calling blocking function from
  439. + * this callback is not allowed.
  440. + *****************************************************************************/
  441. +static OSStatus RenderCallback(vlc_object_t *p_obj,
  442. + AudioUnitRenderActionFlags *ioActionFlags,
  443. + const AudioTimeStamp *inTimeStamp,
  444. + UInt32 inBusNumber,
  445. + UInt32 inNumberFrames,
  446. + AudioBufferList *ioData) {
  447. + VLC_UNUSED(ioActionFlags);
  448. + VLC_UNUSED(inTimeStamp);
  449. + VLC_UNUSED(inBusNumber);
  450. +
  451. + audio_output_t * p_aout = (audio_output_t *)p_obj;
  452. + struct aout_sys_t * p_sys = p_aout->sys;
  453. +
  454. + int bytesToCopy = ioData->mBuffers[0].mDataByteSize;
  455. + Float32 *targetBuffer = (Float32*)ioData->mBuffers[0].mData;
  456. +
  457. + /* Pull audio from buffer */
  458. + int32_t availableBytes = 0;
  459. + Float32 *buffer = TPCircularBufferTail(&p_sys->circular_buffer, &availableBytes);
  460. +
  461. + /* check if we have enough data */
  462. + if (!availableBytes) {
  463. + /* return an empty buffer so silence is played until we have data */
  464. + for (UInt32 j = 0; j < inNumberFrames; j++)
  465. + targetBuffer[j] = 0.;
  466. + } else {
  467. + memcpy(targetBuffer, buffer, __MIN(bytesToCopy, availableBytes));
  468. + TPCircularBufferConsume(&p_sys->circular_buffer, __MIN(bytesToCopy, availableBytes));
  469. + VLC_UNUSED(inNumberFrames);
  470. + vlc_mutex_lock(&p_sys->lock);
  471. + p_sys->i_last_sample_time = inTimeStamp->mSampleTime;
  472. + vlc_mutex_unlock(&p_sys->lock);
  473. + if (p_sys->i_first_time_stamp == 0)
  474. + p_sys->i_first_time_stamp = inTimeStamp->mSampleTime;
  475. + }
  476. +
  477. + return noErr;
  478. +}
  479. +
  480. --
  481. 1.7.12.4 (Apple Git-37)