流媒体播放-在线音乐(1)
流播放文件即用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)相关推荐
- python播放网络音乐_python使用Tkinter实现在线音乐播放器
本文实例使用Tkinter实现在线音乐播放器的具体代码,供大家参考,具体内容如下 1.先使用Tkinter库写界面 2.写点击按钮触发的事件 (1).使用网易音乐的api,返回数据包装成json格式数 ...
- python调用默认播放器_python使用Tkinter实现在线音乐播放器
本文实例使用Tkinter实现在线音乐播放器的具体代码,供大家参考,具体内容如下 1.先使用Tkinter库写界面 2.写点击按钮触发的事件 (1).使用网易音乐的api,返回数据包装成json格式数 ...
- 基于SSH的在线音乐点评网站-java在线音乐点评网站
基于SSH的在线音乐点评网站-java在线音乐点评网站 开发环境:Eclipse for J2EE,MYSQL5.1,JDK1.7,Tomcat 7 首页主要展示一些最新的音乐专辑. (1)用户注册: ...
- springboot+vue在线音乐网站
项目编号:BS-PT-049 项目说明: 本系统基于Springboot和Vue实现的前后端分离的一个在线音乐网站系统,系统功能完整,页面简洁大方,是一个非常优秀的JAVA系统,比较适合做毕业设计使用 ...
- 在线音乐用户寄望用爱发电,资本不愿无米之炊
文/螳螂财经(ID:TanglangFin) 作者/陈小江 想要发现音乐新世界的虾米,最终没能找到新世界. 1月5日,虾米音乐官宣将于2 月5 日停止服务.3月5日0点后,除网页端音螺平台音乐人(即原 ...
- 腾讯“死守”版权,网易云“再强”社区,各筑“孤峰”对战在线音乐下半场
(图片来源于网络) 文 | 易不二 来源 | 螳螂财经 8月8日,网易发布的2019年第二季度财报显示,网易第二季度净收入为187.69亿元人民币,同比增长15.3%,净利润为30.71亿元人民币,同 ...
- 千亿市场竟是蓝海——在线音乐教学产品观察
众所周知,音乐产业与教育产业都是千亿体量市场,而当这两个热点行业交汇,我们惊奇地发现,音乐教育这个行业竟难寻巨头乃至独角兽企业.本文结合网易云信在音乐教育行业的实践,探讨音乐教育行业的深层痛点,讨论音 ...
- 三大技术突破,网易云信实现在线音乐教育极速上线
在线教育因为解决了传统教育难以跨越的时间.空间问题而备受关注.而在线音乐教育作为兴趣教育的一大门类,尤其引人注目. 基于日益扩大的市场需求,网易云信推出了全球首个音乐教学解决方案,下面让我们一起来了解 ...
- China Daily | 技术不是拦路虎:在线音乐教学可完美还原线下场景
随着经济与科技的迅猛发展,移动互联网不断催生出新的音乐教育模式,直播授课.音乐陪练等教学模式日渐兴起,但是在线音乐教育之路不易,技术依然是在线音乐教学发展中的难点.近日,China Daily发文&l ...
- Sony Sony开发在线音乐订阅服务
Sony BMG开发在线音乐订阅服务 [url]http://www.sina.com.cn[/url] 2008年03月26日 01:24 新浪科技 新浪科技讯 北京时间3月26日消息,据国外媒体 ...
最新文章
- Pandas 使用入门
- 我国智能家居行业运行现状分析 标准割裂市场
- javascript基础教程_JavaScript基础教程(九)对象、类的定义与使用
- Elasticsearch原理与调优
- CF446D-DZY Loves Games【高斯消元,矩阵乘法】
- matlab 水文频率曲线,【求助】如何计算水文频率,外行求教 - 地学 - 小木虫 - 学术 科研 互动社区...
- Java程序Date类型比较
- Ceph rbd cmd练习
- 常对象和常函数的关系 const
- 使用range()以相反的顺序打印列表?
- spring入门常见的问题及解决办法
- 敏捷开发框架_RingCentral Tech丨LeSS- 大规模敏捷开发框架实践心路
- 网易2012校园招聘笔试题目
- pandas数据分组聚合——groupby()、aggregate()、apply()、transform()和filter()方法详解
- C#windows图书信息管理系统
- 香港城市大学全球首创3D打印微型机器人技术 有望作治疗癌症用途
- 人人商城-数据选择器
- 飞行员兄弟 JAVA题解
- ARM裸机的知识点总结---------11、iNandFlash (sd卡芯片化)
- 【luogu CF1430A】Number of Apartments(暴力)