X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faudio_output%2Faudioqueue.c;h=c945a85bc7084d81cccc6d31eb9545ed093cc78b;hb=688f5db3c8764faed15a735f987b4fe85413a833;hp=cc33c92e68048d9276e72aefba05b1a05d31f2bc;hpb=bcd01c48635ac900c243fea6e47669db2e350393;p=vlc diff --git a/modules/audio_output/audioqueue.c b/modules/audio_output/audioqueue.c index cc33c92e68..c945a85bc7 100644 --- a/modules/audio_output/audioqueue.c +++ b/modules/audio_output/audioqueue.c @@ -1,69 +1,66 @@ /***************************************************************************** - * audioqueue.c : AudioQueue audio output plugin for vlc - ***************************************************************************** - * Copyright (C) 2010-2012 VLC authors and VideoLAN + * Copyright (C) 2000-2013 VLC authors and VideoLAN * $Id$ * - * Authors: Romain Goyet - * Felix Paul Kühne + * 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 - *****************************************************************************/ +#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 +#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; - bool b_stopped; - float f_volume; -}; + AudioQueueRef audioQueueRef; + AudioQueueTimelineRef timelineRef; -/***************************************************************************** - * Local prototypes - *****************************************************************************/ -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 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")) @@ -74,186 +71,212 @@ add_shortcut("audioqueue") set_callbacks(Open, Close) vlc_module_end () -/***************************************************************************** - * Start: open the audio device - *****************************************************************************/ +#pragma mark - +#pragma mark initialization + +static int Open(vlc_object_t *obj) +{ + audio_output_t *aout = (audio_output_t *)obj; + aout_sys_t *sys = malloc(sizeof (*sys)); + + if (unlikely(sys == NULL)) + return VLC_ENOMEM; + + aout->sys = sys; + aout->start = Start; + aout->stop = Stop; + aout->volume_set = VolumeSet; + + /* reset volume */ + aout_VolumeReport(aout, 1.0); + + return VLC_SUCCESS; +} + +static void Close(vlc_object_t *obj) +{ + audio_output_t *aout = (audio_output_t *)obj; + msg_Dbg( aout, "audioqueue: Close"); + aout_sys_t *sys = aout->sys; + + free(sys); +} + +static int VolumeSet(audio_output_t * p_aout, float volume) +{ + 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; +} static int Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt) { aout_sys_t *p_sys = p_aout->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 - NULL, // User data, passed to the callback - CFRunLoopGetMain(), // RunLoop - kCFRunLoopCommonModes, // RunLoop mode - 0, // Flags ; must be zero (per documentation)... - &(p_sys->audioQueue)); // Output - - msg_Dbg(p_aout, "New AudioQueue output created (status = %i)", status); - if (status != noErr) + 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; + + // 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_S16N; + fmt->i_format = VLC_CODEC_FL32; fmt->i_physical_channels = AOUT_CHANS_STEREO; - fmt->i_rate = 44100; aout_FormatPrepare(fmt); + p_aout->sys->i_rate = fmt->i_rate; - p_aout->sys->b_stopped = false; + // start queue + error = AudioQueueStart(p_sys->audioQueueRef, NULL); + msg_Dbg(p_aout, "Starting AudioQueue (status = %li)", error); - status = AudioQueueStart(p_sys->audioQueue, NULL); - msg_Dbg(p_aout, "Starting AudioQueue (status = %i)", status); + // 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; p_aout->time_get = TimeGet; p_aout->play = Play; p_aout->pause = Pause; p_aout->flush = Flush; - - if (status != noErr) - return VLC_EGENERIC; - return VLC_SUCCESS; } -/***************************************************************************** - * Stop: close the audio device - *****************************************************************************/ - -static void Stop (audio_output_t *p_aout) +static void Stop(audio_output_t *p_aout) { - p_aout->sys->b_stopped = true; + AudioSessionSetActive(false); - msg_Dbg(p_aout, "Stopping AudioQueue"); - AudioQueueStop(p_aout->sys->audioQueue, true); - msg_Dbg(p_aout, "Disposing AudioQueue"); - AudioQueueDispose(p_aout->sys->audioQueue, true); + 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"); } -/***************************************************************************** - * actual playback - *****************************************************************************/ +#pragma mark - +#pragma mark actual playback -static void Play (audio_output_t *p_aout, block_t *p_block) +static void Play(audio_output_t *p_aout, block_t *p_block) { - if (p_block != NULL) { - AudioQueueBufferRef inBuffer = NULL; - OSStatus status; - - status = AudioQueueAllocateBuffer(p_aout->sys->audioQueue, FRAME_SIZE * 2, &inBuffer); - if (status != noErr) { - msg_Err(p_aout, "buffer alloction failed (%i)", status); - return; - } + 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; - block_Release(p_block); - status = AudioQueueEnqueueBuffer(p_aout->sys->audioQueue, inBuffer, 0, NULL); - if (status != noErr) - msg_Err(p_aout, "enqueuing buffer failed (%i)", status); - } + 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 AudioQueueCallback(void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) { +void UnusedAudioQueueCallback(void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) { /* this function does nothing, but needs to be here to make the AudioQueue API happy. - * without a callback, it will refuse to create an AudioQueue instance. */ + * additionally, we clean-up after empty buffers */ VLC_UNUSED(inUserData); - VLC_UNUSED(inAQ); - VLC_UNUSED(inBuffer); + AudioQueueFreeBuffer(inAQ, inBuffer); } -static void Pause (audio_output_t *p_aout, bool pause, mtime_t date) +static void Pause(audio_output_t *p_aout, bool pause, mtime_t date) { VLC_UNUSED(date); - if (pause) - AudioQueuePause(p_aout->sys->audioQueue); - else - AudioQueueStart(p_aout->sys->audioQueue, NULL); + if (pause) { + AudioQueuePause(p_aout->sys->audioQueueRef); + AudioSessionSetActive(false); + } else { + AudioQueueStart(p_aout->sys->audioQueueRef, NULL); + AudioSessionSetActive(true); + } } -static void Flush (audio_output_t *p_aout, bool wait) +static void Flush(audio_output_t *p_aout, bool wait) { - if (p_aout->sys->b_stopped || !p_aout->sys->audioQueue) + if (!p_aout->sys->audioQueueRef) return; + AudioQueueDisposeTimeline(p_aout->sys->audioQueueRef, p_aout->sys->timelineRef); + if (wait) - AudioQueueFlush(p_aout->sys->audioQueue); + AudioQueueStop(p_aout->sys->audioQueueRef, false); else - AudioQueueReset(p_aout->sys->audioQueue); -} - -static int TimeGet (audio_output_t *p_aout, mtime_t *restrict delay) -{ - // TODO - - VLC_UNUSED(p_aout); - VLC_UNUSED(delay); + AudioQueueStop(p_aout->sys->audioQueueRef, true); - return -1; + 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; } -/***************************************************************************** - * Module management - *****************************************************************************/ - -static int VolumeSet(audio_output_t * p_aout, float volume) +static int TimeGet(audio_output_t *p_aout, mtime_t *restrict delay) { - 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->audioQueue, kAudioQueueParam_Volume, volume * volume * volume); + AudioTimeStamp outTimeStamp; + Boolean b_discontinuity; + OSStatus status = AudioQueueGetCurrentTime(p_aout->sys->audioQueueRef, p_aout->sys->timelineRef, &outTimeStamp, &b_discontinuity); - return ostatus; -} - -/***************************************************************************** - * Module management - *****************************************************************************/ - -static int Open(vlc_object_t *obj) -{ - audio_output_t *aout = (audio_output_t *)obj; - aout_sys_t *sys = malloc(sizeof (*sys)); - - if (unlikely(sys == NULL)) - return VLC_ENOMEM; - - aout->sys = sys; - aout->start = Start; - aout->stop = Stop; - aout->volume_set = VolumeSet; + if (status != noErr) + return -1; - /* reset volume */ - aout_VolumeReport(aout, 1.0); + bool b_started = p_aout->sys->b_started; - return VLC_SUCCESS; -} + if (!b_started) + return -1; -static void Close(vlc_object_t *obj) -{ - audio_output_t *aout = (audio_output_t *)obj; - msg_Dbg( aout, "audioqueue: Close"); - aout_sys_t *sys = aout->sys; + if (b_discontinuity) { + msg_Dbg(p_aout, "detected output discontinuity"); + return -1; + } - free(sys); + 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; }