流播放音频

iOS音频播放框架解析
.h文件


#ifdef TARGET_OS_IPHONE 
#import <UIKit/UIKit.h>
#else
#import <Cocoa/Cocoa.h>
#endif TARGET_OS_IPHONE 


#import <AudioToolbox/AudioQueue.h>
#import <AudioToolbox/AudioFile.h>
#include <pthread.h>
#include <AudioToolbox/AudioToolbox.h>


#define NUM_QUEUE_BUFFERS 3
#define kNumAQBufs 6 // number of audio queue buffers we allocate
#define kAQBufSize 32 * 1024 // number of bytes in each audio queue buffer
#define kAQMaxPacketDescs 512 // number of packet descriptions in our array


@interface xxxxx : NSObject {
/*-----------------USED FOR HTTP STREAM------------------*/
NSURL *url;
BOOL isPlaying;

@public
AudioFileStreamID audioFileStream; // the audio file stream parser
AudioStreamPacketDescription packetDescsQueue[kAQMaxPacketDescs]; // packet descriptions for enqueuing audio

CFReadStreamRef stream;

unsigned int fillBufferIndex; // the index of the audioQueueBuffer that is being filled
size_t bytesFilled; // how many bytes have been filled
size_t packetsFilled; // how many packets have been filled

bool inuse[kNumAQBufs]; // flags to indicate that a buffer is still in use
bool started; // flag to indicate that the queue has been started
bool failed; // flag to indicate an error occurred
bool discontinuous; // flag to trigger bug-avoidance

pthread_mutex_t mutex; // a mutex to protect the inuse flags
pthread_cond_t cond; // a condition varable for handling the inuse flags
pthread_mutex_t mutex2; // a mutex to protect the AudioQueue buffer

/*-------------------USED FOR LOCAL FILE--------------------*/
AudioFileID audioFile;
AudioStreamBasicDescription dataFormat;
AudioStreamPacketDescription *packetDescs;

UInt64 packetIndex;
UInt32 numPacketsToRead;

BOOL repeat;
BOOL trackClosed;

/*--------------------USED FOR PUBLIC------------------------*/
BOOL trackEnded;

AudioQueueRef queue;
AudioQueueBufferRef buffers[NUM_QUEUE_BUFFERS];
}


@property BOOL isPlaying;
@property BOOL trackClosed;


- (id) initWithURL:(NSURL*) newURL;
- (id) initWithPath:(NSString*) path;


- (void) setGain:(Float32)gain;
- (void) setRepeat:(BOOL)yn;
- (void) setPlayingWhenAutoLock;


- (void) play;
- (void) playURL;


- (void) pause;
- (void) stopURL;


- (void) close;


extern NSString *xxxTrackFinishedPlayingNotification;


@end



.m文件


#import "xxxxx.h" 
#import <CFNetwork/CFNetwork.h>


static UInt32 kxxxBufferSizeBytes = 0x10000; // 64k
static BOOL kxxxTrackActive = NO;
NSString *xxxTrackFinishedPlayingNotification = @"xxxTrackFinishedPlayingNotification";


#pragma mark -
#pragma mark CFReadStream Callback Function Prototypes


void ReadStreamCallBack(CFReadStreamRef stream, CFStreamEventType eventType, void* dataIn);


#pragma mark -
#pragma mark Audio Callback Function Prototypes


void MyAudioQueueOutputCallback(void* inClientData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer);
void MyAudioQueueIsRunningCallback(void *inUserData, AudioQueueRef inAQ, AudioQueuePropertyID inID);
void MyPropertyListenerProc(void *inClientData, AudioFileStreamID inAudioFileStream, AudioFileStreamPropertyID inPropertyID, UInt32 *ioFlags);
void MyPacketsProc(void *inClientData, UInt32 inNumberBytes, UInt32 inNumberPackets, const void *inInputData, AudioStreamPacketDescription *inPacketDescriptions);
OSStatus MyEnqueueBuffer(xxxxx* myData);


#ifdef TARGET_OS_IPHONE 
void MyAudioSessionInterruptionListener(void *inClientData, UInt32 inInterruptionState);
#endif


#pragma mark -
#pragma mark Audio Callback Function Implementations


//
// MyPropertyListenerProc
//
// Receives notification when the AudioFileStream has audio packets to be
// played. In response, this function creates the AudioQueue, getting it
// ready to begin playback (playback won't begin until audio packets are
// sent to the queue in MyEnqueueBuffer).
//
// This function is adapted from Apple's example in AudioFileStreamExample with
// kAudioQueueProperty_IsRunning listening added.
//
void MyPropertyListenerProc(void *inClientData, AudioFileStreamID inAudioFileStream, AudioFileStreamPropertyID inPropertyID, UInt32 *ioFlags)
{ 
// this is called by audio file stream when it finds property values
xxxxx* myData = (xxxxx*)inClientData;
OSStatus err = noErr;

switch (inPropertyID) {
case kAudioFileStreamProperty_ReadyToProducePackets :
{
myData->discontinuous = true;

// the file stream parser is now ready to produce audio packets.
// get the stream format.
AudioStreamBasicDescription asbd;
UInt32 asbdSize = sizeof(asbd);
err = AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_DataFormat, &asbdSize, &asbd);
if (err) { NSLog(@"get kAudioFileStreamProperty_DataFormat"); myData->failed = true; break; }

// create the audio queue
err = AudioQueueNewOutput(&asbd, MyAudioQueueOutputCallback, myData, NULL, NULL, 0, &myData->queue);
if (err) { NSLog(@"AudioQueueNewOutput"); myData->failed = true; break; }

// listen to the "isRunning" property
err = AudioQueueAddPropertyListener(myData->queue, kAudioQueueProperty_IsRunning, MyAudioQueueIsRunningCallback, myData);
if (err) { NSLog(@"AudioQueueAddPropertyListener"); myData->failed = true; break; }

// allocate audio queue buffers
for (unsigned int i = 0; i < kNumAQBufs; ++i) {
err = AudioQueueAllocateBuffer(myData->queue, kAQBufSize, &myData->buffers);
if (err) { NSLog(@"AudioQueueAllocateBuffer"); myData->failed = true; break; }
}

// get the cookie size
UInt32 cookieSize;
Boolean writable;
err = AudioFileStreamGetPropertyInfo(inAudioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, &writable);
if (err) { NSLog(@"info kAudioFileStreamProperty_MagicCookieData"); break; }

// get the cookie data
void* cookieData = calloc(1, cookieSize);
err = AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, cookieData);
if (err) { NSLog(@"get kAudioFileStreamProperty_MagicCookieData"); free(cookieData); break; }

// set the cookie on the queue.
err = AudioQueueSetProperty(myData->queue, kAudioQueueProperty_MagicCookie, cookieData, cookieSize);
free(cookieData);
if (err) { NSLog(@"set kAudioQueueProperty_MagicCookie"); break; }
break;
}
}
}
复制代码

 

复制代码
//
// MyPacketsProc
//
// When the AudioStream has packets to be played, this function gets an
// idle audio buffer and copies the audio packets into it. The calls to
// MyEnqueueBuffer won't return until there are buffers available (or the
// playback has been stopped).
//
// This function is adapted from Apple's example in AudioFileStreamExample with
// CBR functionality added.
//
void MyPacketsProc(void *inClientData, UInt32 inNumberBytes, UInt32 inNumberPackets, const void *inInputData, AudioStreamPacketDescription *inPacketDescriptions)
{
// this is called by audio file stream when it finds packets of audio
xxxxx* myData = (xxxxx*)inClientData;

// we have successfully read the first packests from the audio stream, so
// clear the "discontinuous" flag
myData->discontinuous = false;

// the following code assumes we're streaming VBR data. for CBR data, the second branch is used.
if (inPacketDescriptions)
{
for (int i = 0; i < inNumberPackets; ++i) {
SInt64 packetOffset = inPacketDescriptions.mStartOffset;
SInt64 packetSize   = inPacketDescriptions.mDataByteSize;

// If the audio was terminated before this point, then
// exit.
if (myData->trackEnded)
{
return;
}

// if the space remaining in the buffer is not enough for this packet, then enqueue the buffer.
size_t bufSpaceRemaining = kAQBufSize - myData->bytesFilled;
if (bufSpaceRemaining < packetSize) {
MyEnqueueBuffer(myData);
}

pthread_mutex_lock(&myData->mutex2);

// If the audio was terminated while waiting for a buffer, then
// exit.
if (myData->trackEnded)
{
pthread_mutex_unlock(&myData->mutex2);
return;
}

// copy data to the audio queue buffer
AudioQueueBufferRef fillBuf = myData->buffers[myData->fillBufferIndex];
memcpy((char*)fillBuf->mAudioData + myData->bytesFilled, (const char*)inInputData + packetOffset, packetSize);

pthread_mutex_unlock(&myData->mutex2);

// fill out packet description
myData->packetDescsQueue[myData->packetsFilled] = inPacketDescriptions;
myData->packetDescsQueue[myData->packetsFilled].mStartOffset = myData->bytesFilled;
// keep track of bytes filled and packets filled
myData->bytesFilled += packetSize;
myData->packetsFilled += 1;

// if that was the last free packet description, then enqueue the buffer.
size_t packetsDescsRemaining = kAQMaxPacketDescs - myData->packetsFilled;
if (packetsDescsRemaining == 0) {
MyEnqueueBuffer(myData);
}
} 
}
else
{
size_t offset = 0;
while (inNumberBytes)
{
// if the space remaining in the buffer is not enough for this packet, then enqueue the buffer.
size_t bufSpaceRemaining = kAQBufSize - myData->bytesFilled;
if (bufSpaceRemaining < inNumberBytes) {
MyEnqueueBuffer(myData);
}

pthread_mutex_lock(&myData->mutex2);

// If the audio was terminated while waiting for a buffer, then
// exit.
if (myData->trackEnded)
{
pthread_mutex_unlock(&myData->mutex2);
return;
}

// copy data to the audio queue buffer
AudioQueueBufferRef fillBuf = myData->buffers[myData->fillBufferIndex];
bufSpaceRemaining = kAQBufSize - myData->bytesFilled;
size_t copySize;
if (bufSpaceRemaining < inNumberBytes)
{
copySize = bufSpaceRemaining;
}
else
{
copySize = inNumberBytes;
}
memcpy((char*)fillBuf->mAudioData + myData->bytesFilled, (const char*)(inInputData + offset), copySize);

pthread_mutex_unlock(&myData->mutex2);

// keep track of bytes filled and packets filled
myData->bytesFilled += copySize;
myData->packetsFilled = 0;
inNumberBytes -= copySize;
offset += copySize;
}
}
}


//
// MyEnqueueBuffer
//
// Called from MyPacketsProc and connectionDidFinishLoading to pass filled audio
// bufffers (filled by MyPacketsProc) to the AudioQueue for playback. This
// function does not return until a buffer is idle for further filling or
// the AudioQueue is stopped.
//
// This function is adapted from Apple's example in AudioFileStreamExample with
// CBR functionality added.
//
OSStatus MyEnqueueBuffer(xxxxx* myData)
{
OSStatus err = noErr;
myData->inuse[myData->fillBufferIndex] = true; // set in use flag

// enqueue buffer
AudioQueueBufferRef fillBuf = myData->buffers[myData->fillBufferIndex];
fillBuf->mAudioDataByteSize = myData->bytesFilled;

if (myData->packetsFilled)
{
err = AudioQueueEnqueueBuffer(myData->queue, fillBuf, myData->packetsFilled, myData->packetDescsQueue);
}
else
{
err = AudioQueueEnqueueBuffer(myData->queue, fillBuf, 0, NULL);
}

if (err) { NSLog(@"AudioQueueEnqueueBuffer"); myData->failed = true; return err; } 

if (!myData->started) { // start the queue if it has not been started already
err = AudioQueueStart(myData->queue, NULL);
if (err) { NSLog(@"AudioQueueStart"); myData->failed = true; return err; } 
myData->started = true;
}

// go to next buffer
if (++myData->fillBufferIndex >= kNumAQBufs) myData->fillBufferIndex = 0;
myData->bytesFilled = 0; // reset bytes filled
myData->packetsFilled = 0; // reset packets filled

// wait until next buffer is not in use
pthread_mutex_lock(&myData->mutex); 
while (myData->inuse[myData->fillBufferIndex] && !myData->trackEnded)
{
pthread_cond_wait(&myData->cond, &myData->mutex);
}
pthread_mutex_unlock(&myData->mutex);

return err;
}


//
// MyFindQueueBuffer
//
// Returns the index of the specified buffer in the audioQueueBuffer array.
//
// This function is unchanged from Apple's example in AudioFileStreamExample.
//
int MyFindQueueBuffer(xxxxx* myData, AudioQueueBufferRef inBuffer)
{
for (unsigned int i = 0; i < kNumAQBufs; ++i) {
if (inBuffer == myData->buffers) 
return i;
}
return -1;
}


//
// MyAudioQueueOutputCallback
//
// Called from the AudioQueue when playback of specific buffers completes. This
// function signals from the AudioQueue thread to the AudioStream thread that
// the buffer is idle and available for copying data.
//
// This function is unchanged from Apple's example in AudioFileStreamExample.
//
void MyAudioQueueOutputCallback(void* inClientData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
{
// this is called by the audio queue when it has finished decoding our data. 
// The buffer is now free to be reused.
xxxxx* myData = (xxxxx*)inClientData;
unsigned int bufIndex = MyFindQueueBuffer(myData, inBuffer);

// signal waiting thread that the buffer is free.
pthread_mutex_lock(&myData->mutex);
myData->inuse[bufIndex] = false;
pthread_cond_signal(&myData->cond);
pthread_mutex_unlock(&myData->mutex);
}


//
// MyAudioQueueIsRunningCallback
//
// Called from the AudioQueue when playback is started or stopped. This
// information is used to toggle the observable "isPlaying" property and
// set the "finished" flag.
//
void MyAudioQueueIsRunningCallback(void *inUserData, AudioQueueRef inAQ, AudioQueuePropertyID inID)
{
xxxxx *myData = (xxxxx *)inUserData;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

if (myData.isPlaying)
{
myData->trackEnded = true;
myData.isPlaying = false;

#ifdef TARGET_OS_IPHONE 
AudioSessionSetActive(false);
#endif
}
else
{
myData.isPlaying = true;
if (myData->trackEnded)
{
myData.isPlaying = false;
}

//
// Note about this bug avoidance quirk:
//
// On cleanup of the AudioQueue thread, on rare occasions, there would
// be a crash in CFSetContainsValue as a CFRunLoopObserver was getting
// removed from the CFRunLoop.
//
// After lots of testing, it appeared that the audio thread was
// attempting to remove CFRunLoop observers from the CFRunLoop after the
// thread had already deallocated the run loop.
//
// By creating an NSRunLoop for the AudioQueue thread, it changes the
// thread destruction order and seems to avoid this crash bug -- or
// at least I haven't had it since (nasty hard to reproduce error!)
//
[NSRunLoop currentRunLoop];
}

[pool release];
}


#ifdef TARGET_OS_IPHONE 
//
// MyAudioSessionInterruptionListener
//
// Invoked if the audio session is interrupted (like when the phone rings)
//
void MyAudioSessionInterruptionListener(void *inClientData, UInt32 inInterruptionState)
{
}
#endif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值