流播放文件即用AudioStream 和 AudioQueue 来播放文件。好处是可以快速的开始播放,减少读文件的过程,适合大文件特别是背景音乐的播放。坏处是一次只能播放一个文件,如果要换播放文件,中间需要一定的时间。但是因为iPhone的文件读取时间只有10秒,对于资源较大的文件,只能考虑这个方式了。下面我将分享一下我在这方面的一点经验:

1. 单个文件播放2. 在线文件播放

1. 单个文件播放 BOOL isPlaying;

AudioFileID audioFile;

AudioStreamBasicDescription dataFormat;

AudioStreamPacketDescription *packetDescs;

UInt64 packetIndex;

UInt32 numPacketsToRead;

BOOL repeat;

BOOL trackClosed;

BOOL trackEnded;

AudioQueueRef queue;

AudioQueueBufferRef buffers[NUM_QUEUE_BUFFERS];

以上是需要定义的为单独文件播放的所需要的元素。可以定义在类里面。

2. 在线文件播放

NSURL *url;

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

BOOL trackEnded;

AudioQueueRef queue;

AudioQueueBufferRef buffers[NUM_QUEUE_BUFFERS];

利用http1.1协议播放在线文件。以上是在线文件播放所需要的参数。

#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

这里是定义的一些参数,NUM_QUEUE_BUFFERS 用于播放本地文件,而 kNumAQBufs 用于播放在线文件。

3. 本地文件初始化

- (id)initWithPath:(NSString*)path

{

UInt32 size, maxPacketSize;

char *cookie;

int i;

if (kxxxTrackActive)

{

NSLog(@"Other music is playing.");

return nil;

}

if (path == nil) return nil;

if(!(self = [super init])) return nil;

// try to open up the file using the specified path

if (noErr != AudioFileOpenURL((CFURLRef)[NSURL fileURLWithPath:path], 0x01, 0, &audioFile))

{

NSLog(@"File can not be opened!");

return nil;

}

// get the data format of the file

size = sizeof(dataFormat);

AudioFileGetProperty(audioFile, kAudioFilePropertyDataFormat, &size, &dataFormat);

// create a new playback queue using the specified data format and buffer callback

AudioQueueNewOutput(&dataFormat, BufferCallback, self, nil, nil, 0, &queue);

// calculate number of packets to read and allocate space for packet descriptions if needed

if (dataFormat.mBytesPerPacket == 0 || dataFormat.mFramesPerPacket == 0)

{

// Ask Core Audio to give us a conservative estimate of the largest packet

size = sizeof(maxPacketSize);

AudioFileGetProperty(audioFile, kAudioFilePropertyPacketSizeUpperBound, &size, &maxPacketSize);

if (maxPacketSize > kxxxBufferSizeBytes)

{

maxPacketSize = kxxxBufferSizeBytes;

NSLog(@"Size out of bounds!");

}

// calculate how many packs to read

numPacketsToRead = kxxxBufferSizeBytes / maxPacketSize;

// will need a packet description for each packet to allocate space accordingly

packetDescs = malloc(sizeof(AudioStreamPacketDescription) * numPacketsToRead);

}

else

{

// constant bitrate

numPacketsToRead = kxxxBufferSizeBytes / dataFormat.mBytesPerPacket;

// don't need packet descriptions for CBR data

packetDescs = nil;

}

// see if file uses a magic cookie (a magic cookie is meta data which some formats use)

AudioFileGetPropertyInfo(audioFile, kAudioFilePropertyMagicCookieData, &size, nil);

if (size > 0)

{

// copy the cookie data from the file into the audio queue

cookie = malloc(sizeof(char) * size);

AudioFileGetProperty(audioFile, kAudioFilePropertyMagicCookieData, &size, cookie);

AudioQueueSetProperty(queue, kAudioQueueProperty_MagicCookie, cookie, size);

free(cookie);

}

// we want to know when the playing state changes so we can properly dispose of the audio queue when it's done

AudioQueueAddPropertyListener(queue, kAudioQueueProperty_IsRunning, propertyListenerCallback, self);

// allocate and prime buffers with some data

packetIndex = 0;

for (i = 0; i < NUM_QUEUE_BUFFERS; i++)

{

AudioQueueAllocateBuffer(queue, kxxxBufferSizeBytes, &buffers);

if ([self readPacketsIntoBuffer:buffers] == 0)

{

// this might happen if the file was so short that it needed less buffers than we planned on using

break;

}

}

repeat = NO;

trackClosed = NO;

trackEnded = NO;

kxxxTrackActive = YES;

return self;

}

4. 在线文件初始化

- (id)initWithURL:(NSURL*)newUrl

{

self = [super init];

if (self != nil)

{

url = [newUrl retain];

}

return self;

}

算了,废话不多说了,直接上代码,等以后有时间了再逐一解释。

.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 {

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

AudioFileID audioFile;

AudioStreamBasicDescription dataFormat;

AudioStreamPacketDescription *packetDescs;

UInt64 packetIndex;

UInt32 numPacketsToRead;

BOOL repeat;

BOOL trackClosed;

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;

}

流媒体播放-在线音乐(1)相关推荐

  1. python播放网络音乐_python使用Tkinter实现在线音乐播放器

    本文实例使用Tkinter实现在线音乐播放器的具体代码,供大家参考,具体内容如下 1.先使用Tkinter库写界面 2.写点击按钮触发的事件 (1).使用网易音乐的api,返回数据包装成json格式数 ...

  2. python调用默认播放器_python使用Tkinter实现在线音乐播放器

    本文实例使用Tkinter实现在线音乐播放器的具体代码,供大家参考,具体内容如下 1.先使用Tkinter库写界面 2.写点击按钮触发的事件 (1).使用网易音乐的api,返回数据包装成json格式数 ...

  3. 基于SSH的在线音乐点评网站-java在线音乐点评网站

    基于SSH的在线音乐点评网站-java在线音乐点评网站 开发环境:Eclipse for J2EE,MYSQL5.1,JDK1.7,Tomcat 7 首页主要展示一些最新的音乐专辑. (1)用户注册: ...

  4. springboot+vue在线音乐网站

    项目编号:BS-PT-049 项目说明: 本系统基于Springboot和Vue实现的前后端分离的一个在线音乐网站系统,系统功能完整,页面简洁大方,是一个非常优秀的JAVA系统,比较适合做毕业设计使用 ...

  5. 在线音乐用户寄望用爱发电,资本不愿无米之炊

    文/螳螂财经(ID:TanglangFin) 作者/陈小江 想要发现音乐新世界的虾米,最终没能找到新世界. 1月5日,虾米音乐官宣将于2 月5 日停止服务.3月5日0点后,除网页端音螺平台音乐人(即原 ...

  6. 腾讯“死守”版权,网易云“再强”社区,各筑“孤峰”对战在线音乐下半场

    (图片来源于网络) 文 | 易不二 来源 | 螳螂财经 8月8日,网易发布的2019年第二季度财报显示,网易第二季度净收入为187.69亿元人民币,同比增长15.3%,净利润为30.71亿元人民币,同 ...

  7. 千亿市场竟是蓝海——在线音乐教学产品观察

    众所周知,音乐产业与教育产业都是千亿体量市场,而当这两个热点行业交汇,我们惊奇地发现,音乐教育这个行业竟难寻巨头乃至独角兽企业.本文结合网易云信在音乐教育行业的实践,探讨音乐教育行业的深层痛点,讨论音 ...

  8. 三大技术突破,网易云信实现在线音乐教育极速上线

    在线教育因为解决了传统教育难以跨越的时间.空间问题而备受关注.而在线音乐教育作为兴趣教育的一大门类,尤其引人注目. 基于日益扩大的市场需求,网易云信推出了全球首个音乐教学解决方案,下面让我们一起来了解 ...

  9. China Daily | 技术不是拦路虎:在线音乐教学可完美还原线下场景

    随着经济与科技的迅猛发展,移动互联网不断催生出新的音乐教育模式,直播授课.音乐陪练等教学模式日渐兴起,但是在线音乐教育之路不易,技术依然是在线音乐教学发展中的难点.近日,China Daily发文&l ...

  10. Sony Sony开发在线音乐订阅服务

    Sony BMG开发在线音乐订阅服务 [url]http://www.sina.com.cn[/url] 2008年03月26日 01:24  新浪科技 新浪科技讯 北京时间3月26日消息,据国外媒体 ...

最新文章

  1. Pandas 使用入门
  2. 我国智能家居行业运行现状分析 标准割裂市场
  3. javascript基础教程_JavaScript基础教程(九)对象、类的定义与使用
  4. Elasticsearch原理与调优
  5. CF446D-DZY Loves Games【高斯消元,矩阵乘法】
  6. matlab 水文频率曲线,【求助】如何计算水文频率,外行求教 - 地学 - 小木虫 - 学术 科研 互动社区...
  7. Java程序Date类型比较
  8. Ceph rbd cmd练习
  9. 常对象和常函数的关系 const
  10. 使用range()以相反的顺序打印列表?
  11. spring入门常见的问题及解决办法
  12. 敏捷开发框架_RingCentral Tech丨LeSS- 大规模敏捷开发框架实践心路
  13. 网易2012校园招聘笔试题目
  14. pandas数据分组聚合——groupby()、aggregate()、apply()、transform()和filter()方法详解
  15. C#windows图书信息管理系统
  16. 香港城市大学全球首创3D打印微型机器人技术 有望作治疗癌症用途
  17. 人人商城-数据选择器
  18. 飞行员兄弟 JAVA题解
  19. ARM裸机的知识点总结---------11、iNandFlash (sd卡芯片化)
  20. 【luogu CF1430A】Number of Apartments(暴力)

热门文章

  1. html5处理json数据,js解析json数组
  2. 设计师:设计师知识储备之常用标准尺寸参考(墙面尺寸、室内尺寸,餐厅、商场营业厅、饭店客房、卫生间、会议室、交通空间、灯具、办公家具、室内家具等)之详细攻略
  3. 赵本山给NBA球员出脑筋急转弯
  4. Latex数学公式-求和符号上下限的限制
  5. Java实现电商秒杀系统-jseckill
  6. android 截取图片部分,Android 截取图片正中间
  7. 006表格布局管理器
  8. Kubernetes 常见运维技巧
  9. 【Blender】摄像机景深介绍
  10. 带设计师去选材料_装修要找设计师问清楚一些材料选择