X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faudio_output%2Faudioqueue.c;h=c945a85bc7084d81cccc6d31eb9545ed093cc78b;hb=39c41fec23535c2d5ed72016a6a9d9f2fbf5852c;hp=d3d0e0babd10c565eac42ea4de4352681cbc94a1;hpb=fa5495cf0186cd8fe886bafc57582d5e5d34a5e3;p=vlc diff --git a/modules/audio_output/audioqueue.c b/modules/audio_output/audioqueue.c index d3d0e0babd..c945a85bc7 100644 --- a/modules/audio_output/audioqueue.c +++ b/modules/audio_output/audioqueue.c @@ -1,176 +1,282 @@ /***************************************************************************** - * audioqueue.c : AudioQueue audio output plugin for vlc - ***************************************************************************** - * Copyright (C) 2010 VideoLAN and AUTHORS + * Copyright (C) 2000-2013 VLC authors and VideoLAN + * $Id$ * - * Authors: Romain Goyet + * Authors: Felix Paul Kühne + * Rémi Denis-Courmont + * Rafaël Carré * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ -/***************************************************************************** - * Preamble - *****************************************************************************/ -#include /* write(), close() */ +#pragma mark includes #ifdef HAVE_CONFIG_H -# include "config.h" +# import "config.h" +#endif +#import +#import +#import +#import +#import +#if TARGET_OS_IPHONE +#import +#else +#define AudioSessionSetActive(x) #endif -#include -#include -#include - -#include - -#define FRAME_SIZE 2048 -#define NUMBER_OF_BUFFERS 3 +#pragma mark - +#pragma mark private declarations -/***************************************************************************** - * aout_sys_t: AudioQueue audio output method descriptor - ***************************************************************************** - * This structure is part of the audio output thread descriptor. - * It describes the specific properties of an audio device. - *****************************************************************************/ struct aout_sys_t { - AudioQueueRef audioQueue; -}; + AudioQueueRef audioQueueRef; + AudioQueueTimelineRef timelineRef; -/***************************************************************************** - * Local prototypes - *****************************************************************************/ -static int Open ( vlc_object_t * ); -static void Close ( vlc_object_t * ); -static void Play ( aout_instance_t * ); -static void AudioQueueCallback (void *, AudioQueueRef, AudioQueueBufferRef); + bool b_started; -/***************************************************************************** - * Module descriptor - *****************************************************************************/ + mtime_t i_played_length; + int i_rate; + float f_volume; +}; +static int Open (vlc_object_t *); +static void Close (vlc_object_t *); +static void Play (audio_output_t *, block_t *); +static void Pause (audio_output_t *p_aout, bool pause, mtime_t date); +static void Flush (audio_output_t *p_aout, bool wait); +static int TimeGet (audio_output_t *aout, mtime_t *); +static void UnusedAudioQueueCallback (void *, AudioQueueRef, AudioQueueBufferRef); +static int Start(audio_output_t *, audio_sample_format_t *); +static void Stop(audio_output_t *); +static int VolumeSet(audio_output_t *, float ); vlc_module_begin () - set_shortname( "AudioQueue" ) - set_description( N_("AudioQueue (iOS / Mac OS) audio output") ) - set_capability( "audio output", 40 ) - set_category( CAT_AUDIO ) - set_subcategory( SUBCAT_AUDIO_AOUT ) - add_shortcut( "audioqueue" ) - set_callbacks( Open, Close ) +set_shortname("AudioQueue") +set_description(N_("AudioQueue (iOS / Mac OS) audio output")) +set_capability("audio output", 40) +set_category(CAT_AUDIO) +set_subcategory(SUBCAT_AUDIO_AOUT) +add_shortcut("audioqueue") +set_callbacks(Open, Close) vlc_module_end () -/***************************************************************************** - * Open: open the audio device - *****************************************************************************/ +#pragma mark - +#pragma mark initialization -static int Open ( vlc_object_t *p_this ) +static int Open(vlc_object_t *obj) { - aout_instance_t *p_aout = (aout_instance_t *)p_this; - struct aout_sys_t *p_sys = malloc(sizeof(aout_sys_t)); - p_aout->output.p_sys = p_sys; - - OSStatus status = 0; - - // Setup the audio device. - AudioStreamBasicDescription deviceFormat; - deviceFormat.mSampleRate = 44100; - deviceFormat.mFormatID = kAudioFormatLinearPCM; - deviceFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; // Signed integer, little endian - deviceFormat.mBytesPerPacket = 4; - deviceFormat.mFramesPerPacket = 1; - deviceFormat.mBytesPerFrame = 4; - deviceFormat.mChannelsPerFrame = 2; - deviceFormat.mBitsPerChannel = 16; - deviceFormat.mReserved = 0; - - // Create a new output AudioQueue for the device. - status = AudioQueueNewOutput(&deviceFormat, // Format - AudioQueueCallback, // Callback - p_aout, // User data, passed to the callback - CFRunLoopGetMain(), // RunLoop - kCFRunLoopDefaultMode, // RunLoop mode - 0, // Flags ; must be zero (per documentation)... - &(p_sys->audioQueue)); // Output - - // This will be used for boosting the audio without the need of a mixer (floating-point conversion is expensive on ARM) - // AudioQueueSetParameter(p_sys->audioQueue, kAudioQueueParam_Volume, 12.0); // Defaults to 1.0 - - msg_Dbg(p_aout, "New AudioQueue output created (status = %i)", status); - - // Allocate buffers for the AudioQueue, and pre-fill them. - for (int i = 0; i < NUMBER_OF_BUFFERS; ++i) { - AudioQueueBufferRef buffer = NULL; - status = AudioQueueAllocateBuffer(p_sys->audioQueue, FRAME_SIZE * 4, &buffer); - AudioQueueCallback(NULL, p_sys->audioQueue, buffer); - } + audio_output_t *aout = (audio_output_t *)obj; + aout_sys_t *sys = malloc(sizeof (*sys)); - /* Volume is entirely done in software. */ - aout_VolumeSoftInit( p_aout ); + if (unlikely(sys == NULL)) + return VLC_ENOMEM; - p_aout->output.output.i_format = VLC_CODEC_S16L; - p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; - p_aout->output.output.i_rate = 44100; - p_aout->output.i_nb_samples = FRAME_SIZE; - p_aout->output.pf_play = Play; - p_aout->output.pf_pause = NULL; + aout->sys = sys; + aout->start = Start; + aout->stop = Stop; + aout->volume_set = VolumeSet; - msg_Dbg(p_aout, "Starting AudioQueue (status = %i)", status); - status = AudioQueueStart(p_sys->audioQueue, NULL); + /* reset volume */ + aout_VolumeReport(aout, 1.0); return VLC_SUCCESS; } -/***************************************************************************** - * Play: play a sound samples buffer - *****************************************************************************/ -static void Play( aout_instance_t * p_aout ) +static void Close(vlc_object_t *obj) { - VLC_UNUSED(p_aout); + audio_output_t *aout = (audio_output_t *)obj; + msg_Dbg( aout, "audioqueue: Close"); + aout_sys_t *sys = aout->sys; + + free(sys); } -/***************************************************************************** - * Close: close the audio device - *****************************************************************************/ -static void Close ( vlc_object_t *p_this ) +static int VolumeSet(audio_output_t * p_aout, float volume) { - aout_instance_t *p_aout = (aout_instance_t *)p_this; - struct aout_sys_t * p_sys = p_aout->output.p_sys; - - msg_Dbg(p_aout, "Stopping AudioQueue"); - AudioQueueStop(p_sys->audioQueue, false); - msg_Dbg(p_aout, "Disposing of AudioQueue"); - AudioQueueDispose(p_sys->audioQueue, false); - free (p_sys); + struct aout_sys_t *p_sys = p_aout->sys; + OSStatus ostatus; + + aout_VolumeReport(p_aout, volume); + p_sys->f_volume = volume; + + /* Set volume for output unit */ + ostatus = AudioQueueSetParameter(p_sys->audioQueueRef, kAudioQueueParam_Volume, volume * volume * volume); + + return ostatus; } -void AudioQueueCallback(void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) { - aout_instance_t * p_aout = (aout_instance_t *)inUserData; - aout_buffer_t * p_buffer = NULL; +static int Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt) +{ + aout_sys_t *p_sys = p_aout->sys; + OSStatus error = 0; + + // prepare the format description for our output + AudioStreamBasicDescription streamDescription; + streamDescription.mSampleRate = fmt->i_rate; + streamDescription.mFormatID = kAudioFormatLinearPCM; + streamDescription.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; // FL32 + streamDescription.mFramesPerPacket = 1; + streamDescription.mChannelsPerFrame = 2; + streamDescription.mBitsPerChannel = 32; + streamDescription.mBytesPerFrame = streamDescription.mBitsPerChannel * streamDescription.mChannelsPerFrame / 8; + streamDescription.mBytesPerPacket = streamDescription.mBytesPerFrame * streamDescription.mFramesPerPacket; - if (p_aout) { - vlc_mutex_lock( &p_aout->lock ); - p_buffer = aout_FifoPop( &p_aout->output.fifo ); - vlc_mutex_unlock( &p_aout->lock ); - } + // init new output instance + error = AudioQueueNewOutput(&streamDescription, // Format + UnusedAudioQueueCallback, // Unused Callback, which needs to be provided to have a proper instance + NULL, // User data, passed to the callback + NULL, // RunLoop + kCFRunLoopCommonModes, // RunLoop mode + 0, // Flags ; must be zero (per documentation)... + &(p_sys->audioQueueRef)); // Output + msg_Dbg(p_aout, "New AudioQueue instance created (status = %li)", error); + if (error != noErr) + return VLC_EGENERIC; + fmt->i_format = VLC_CODEC_FL32; + fmt->i_physical_channels = AOUT_CHANS_STEREO; + aout_FormatPrepare(fmt); + p_aout->sys->i_rate = fmt->i_rate; + + // start queue + error = AudioQueueStart(p_sys->audioQueueRef, NULL); + msg_Dbg(p_aout, "Starting AudioQueue (status = %li)", error); + + // start timeline for synchro + error = AudioQueueCreateTimeline(p_sys->audioQueueRef, &p_sys->timelineRef); + msg_Dbg(p_aout, "AudioQueue Timeline started (status = %li)", error); + if (error != noErr) + return VLC_EGENERIC; + +#if TARGET_OS_IPHONE + // start audio session so playback continues if mute switch is on + AudioSessionInitialize (NULL, + kCFRunLoopCommonModes, + NULL, + NULL); + + // Set audio session to mediaplayback + UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback; + AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(sessionCategory),&sessionCategory); + AudioSessionSetActive(true); +#endif + + p_aout->sys->b_started = true; - if ( p_buffer != NULL ) { - vlc_memcpy( inBuffer->mAudioData, p_buffer->p_buffer, p_buffer->i_buffer ); - inBuffer->mAudioDataByteSize = p_buffer->i_buffer; - aout_BufferFree( p_buffer ); + p_aout->time_get = TimeGet; + p_aout->play = Play; + p_aout->pause = Pause; + p_aout->flush = Flush; + return VLC_SUCCESS; +} + +static void Stop(audio_output_t *p_aout) +{ + AudioSessionSetActive(false); + + p_aout->sys->i_played_length = 0; + AudioQueueDisposeTimeline(p_aout->sys->audioQueueRef, p_aout->sys->timelineRef); + AudioQueueStop(p_aout->sys->audioQueueRef, true); + AudioQueueDispose(p_aout->sys->audioQueueRef, true); + msg_Dbg(p_aout, "audioqueue stopped and disposed"); +} + +#pragma mark - +#pragma mark actual playback + +static void Play(audio_output_t *p_aout, block_t *p_block) +{ + AudioQueueBufferRef inBuffer = NULL; + OSStatus status; + + status = AudioQueueAllocateBuffer(p_aout->sys->audioQueueRef, p_block->i_buffer, &inBuffer); + if (status == noErr) { + memcpy(inBuffer->mAudioData, p_block->p_buffer, p_block->i_buffer); + inBuffer->mAudioDataByteSize = p_block->i_buffer; + + status = AudioQueueEnqueueBuffer(p_aout->sys->audioQueueRef, inBuffer, 0, NULL); + if (status == noErr) + p_aout->sys->i_played_length += p_block->i_length; + else + msg_Err(p_aout, "enqueuing buffer failed (%li)", status); + } else + msg_Err(p_aout, "buffer alloction failed (%li)", status); + + block_Release(p_block); +} + +void UnusedAudioQueueCallback(void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) { + /* this function does nothing, but needs to be here to make the AudioQueue API happy. + * additionally, we clean-up after empty buffers */ + VLC_UNUSED(inUserData); + AudioQueueFreeBuffer(inAQ, inBuffer); +} + +static void Pause(audio_output_t *p_aout, bool pause, mtime_t date) +{ + VLC_UNUSED(date); + + if (pause) { + AudioQueuePause(p_aout->sys->audioQueueRef); + AudioSessionSetActive(false); } else { - vlc_memset( inBuffer->mAudioData, 0, inBuffer->mAudioDataBytesCapacity ); - inBuffer->mAudioDataByteSize = inBuffer->mAudioDataBytesCapacity; + AudioQueueStart(p_aout->sys->audioQueueRef, NULL); + AudioSessionSetActive(true); + } +} + +static void Flush(audio_output_t *p_aout, bool wait) +{ + if (!p_aout->sys->audioQueueRef) + return; + + AudioQueueDisposeTimeline(p_aout->sys->audioQueueRef, p_aout->sys->timelineRef); + + if (wait) + AudioQueueStop(p_aout->sys->audioQueueRef, false); + else + AudioQueueStop(p_aout->sys->audioQueueRef, true); + + p_aout->sys->b_started = false; + p_aout->sys->i_played_length = 0; + AudioQueueStart(p_aout->sys->audioQueueRef, NULL); + AudioQueueCreateTimeline(p_aout->sys->audioQueueRef, &p_aout->sys->timelineRef); + p_aout->sys->b_started = true; +} + +static int TimeGet(audio_output_t *p_aout, mtime_t *restrict delay) +{ + AudioTimeStamp outTimeStamp; + Boolean b_discontinuity; + OSStatus status = AudioQueueGetCurrentTime(p_aout->sys->audioQueueRef, p_aout->sys->timelineRef, &outTimeStamp, &b_discontinuity); + + if (status != noErr) + return -1; + + bool b_started = p_aout->sys->b_started; + + if (!b_started) + return -1; + + if (b_discontinuity) { + msg_Dbg(p_aout, "detected output discontinuity"); + return -1; } - AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL); + + mtime_t i_pos = (mtime_t) outTimeStamp.mSampleTime * CLOCK_FREQ / p_aout->sys->i_rate; + if (i_pos > 0) { + *delay = p_aout->sys->i_played_length - i_pos; + return 0; + } else + return -1; }