iOS在线音频流播放

前言

这是一篇关于在线音频播放的文章,参考自苹果OS X的demo。 在移植到iOS后,可以通过iphone播放Mac上面的音频,实现在线播放音频的功能。 本文可以学习到socket编程AudioFileStream转换音频流AudioQueue播放音频信号量的使用

正文

demo有两个工程,分别是serversclient。 servers是OS X的应用,作为服务端,负责发送音频流数据; client是iOS的应用,作为客户端,负责接收音频流数据; 音频数据通过AudioFileStream转换后,调用AudioQueue进行播放,中间会用到信号量进行等待和同步。

1、socket编程

bind方法用于绑定接口,然后用listen监听tcp连接请求,accept用于接受tcp连接; fopen打开音频文件,fread读取音频数据,send对建立的连接发送音频流;

对已经失效的socket,send两次数据就会触发SIGPIPE信号,默认的处理是关闭进程。

// 打开文件
FILE* file = fopen([[[NSBundle mainBundle] pathForResource:@"chenli" ofType:@"mp3"] UTF8String], "r");
// 创建socket
int listener_socket = socket(AF_INET, SOCK_STREAM, 0);
// 绑定socket
bind(listener_socket, (struct sockaddr*)&server_sockaddr, sizeof(server_sockaddr));
// 监听tcp连接
listen(listener_socket, 4);
// 接收tcp连接,注意!这里并不是三次握手。
int connection_socket = accept(listener_socket, (struct sockaddr*)&client_sockaddr, &client_sockaddr_size);
// 读取文件
size_t bytesRead = fread(buf, 1, 32768, file);
// 发送音频流
ssize_t bytesSent = send(connection_socket, buf, bytesRead, 0);
// 关闭socket
close(connection_socket);
2、AudioQueue播放音频

AudioQueue的播放时,需要先给audioBuffer填充数据,并把audioBuffer放入AudioQueue,然后通知AudioQueue开始播放; AudioQueue从已经填充的audioBuffer里面开始播放数据,实时把播放完毕的audioBuffer回调给业务层,业务继续填充播放完毕的audioBuffer,重复流程直到音频播放完毕。

前文使用AudioToolbox播放AAC有对AudioQueue更详细的介绍以及更简化的demo。

配置AudioQueue

// 添加AudioQueue的回调函数和添加参数,MyAudioQueueOutputCallback是播完结束的回调
AudioQueueNewOutput(&asbd, MyAudioQueueOutputCallback, myData, NULL, NULL, 0, &audioQueue);
// AudioBuffer分配buffer
AudioQueueAllocateBuffer(audioQueue, kAQBufSize, &audioQueueBuffer[i]);
// 添加AudioQueue的属性监听
AudioQueueAddPropertyListener(audioQueue, kAudioQueueProperty_IsRunning, MyAudioQueueIsRunningCallback, myData);

开始播放

// 开始AudioQueue播放
AudioQueueStart(myData->audioQueue, NULL);
// 向AudioQueue传入buffer
AudioQueueEnqueueBuffer(audioQueue, fillBuf, (UInt32)myData->packetsFilled, packetDescs);

播放结束

// 传入最后的音频数据后需要调用,否则buffer里面的数据可能会影响下次播放
AudioQueueFlush(audioQueue);
// 如果需要停止播放,可以调用这个函数,第二个参数表示同步/异步
AudioQueueStop(audioQueue, false);
// 播放完毕,销毁队列
AudioQueueDispose(audioQueue, false);
3、互斥锁

普通锁 pthread_mutex_lock(mutex) 加锁,可能会阻塞; pthread_mutex_unlock(mutex) 解锁;

条件锁(pthread_cond_wait) 调用pthread_cond_wait时,条件不成立则阻塞,直到条件成立; 调用pthread_cond_wait前,要先调用pthread_mutex_lock(mutex)加锁,pthread_cond_wait会在调用结束解锁mutex; pthread_cond_wait条件满足后(pthread_cond_signal被调用),会对mutex加锁,当我们执行完程序时需要对mutex解锁;

调用pthread_cond_wait时,为了防止并发放入阻塞队列,所以需要提前对mutex加锁;

申请条件锁

pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);

释放条件锁

pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
4、AudioFileStream转换音频流

AudioFileStream可以用来读取音频流信息和分离音频帧,与之类似的API簇还有AudioFile和ExtAudioFile。 AudioFileStream可以用在线音频流,也可以使用本地文件。

// 打开一个音频流转换器,需要设置AudioFileStream_PropertyListenerProc 和 AudioFileStream_PacketsProc 回调函数;
AudioFileStreamOpen(myData, MyPropertyListenerProc, MyPacketsProc, kAudioFileAAC_ADTSType, &audioFileStream);

// AudioFileStreamParseBytes 解析数据,会调用之前设置好的AudioFileStream_PropertyListenerProc 和 AudioFileStream_PacketsProc 回调函数;
AudioFileStreamParseBytes(myData->audioFileStream, (UInt32)bytesRecvd, buf, 0);

// 获取特定的属性
AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_DataFormat, &asbdSize, &asbd);

// 关闭音频流
AudioFileStreamClose(audioFileStream);

附录

demo中用到用到的一些方法: AudioFileStreamParseBytes 解析数据,会调用之前设置好的AudioFileStream_PropertyListenerProcAudioFileStream_PacketsProc 回调函数; AudioFileStreamOpen 打开一个音频流转换器,需要设置AudioFileStream_PropertyListenerProcAudioFileStream_PacketsProc 回调函数; MyPropertyListenerProc 音频属性回调函数; MyPacketsProc 数据回调函数; MyEnqueueBuffer 把buffer里面的数据传入AudioQueue; WaitForFreeBuffer 当前所有buffer已经占用满,等待AudioQueue播放完释放buffer; MyAudioQueueOutputCallback AudioQueue释放buffer的回调函数; MyAudioQueueIsRunningCallback AudioQueue是否在播放的回调函数; MyConnectSocket 建立socket链接 demo 的代码地址在这里传送门

demo的打开方式: server是服务端,运行在OS X 有binary和app两种方式

  • binary需要编译完之后,找到二进制所在的目录,在其目录下放对应的音频文件;
  • app打开,保持运行;

client是客户端,运行在iOS

  • 1、在getHostName处需要修改为OS X的ip地址;
  • 2、iOS和OS X需要处于同一局域网;
  • 3、clietn未播放完结束,会导致server关闭;

总结

这个demo很有意思:用到很多知识点,而且很简单,非常适合学习。 最近越来越忙,如果有问题可以评论或者简信联系,尽量清楚点描述问题还有问题的上下文。

前文系列,或许会有兴趣。 使用VideoToolbox硬编码H.264 使用VideoToolbox硬解码H.264 使用AudioToolbox编码AAC 使用AudioToolbox播放AAC HLS点播实现(H.264和AAC码流) HLS推流的实现(iOS和OS X系统)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏前端真相

PTC(pay to click)—— 躺着赚钱(比特币)?

1827
来自专栏漫漫前端路

Vue 2.3、2.4 知识点小结

2.3 参考 github.com/vuejs/vue/r… 2.4 参考 github.com/vuejs/vue/r… 实例 demo 地址:github....

2502
来自专栏大数据挖掘DT机器学习

数据挖掘工程师:如何通过百度地图API抓取建筑物周边位置、房价信息

1.需求描述 对于数据挖掘工程师来说,有时候需要抓取地理位置信息,比如统计房子周边基础设施信息,比如医院、公交车站、写字楼、地铁站、商场等,一般的爬虫可以采用...

5219
来自专栏武军超python专栏

2018年8月6日初次写飞机大战的总结

ubuntu中安装pygame的步骤: 先安装pip3: apt-get install pip3 再安装pygame: pip3 install p...

2163
来自专栏前端架构与工程

《微信小程序七日谈》- 第一天:人生若只如初见

《微信小程序七日谈》系列文章: 本系列的文章并非初学教程,而是笔者在具体开发过程中遇到的问题以及部分解决方案。 微信小程序自公布以来就被捧上了天,新闻一波接一...

2228
来自专栏知晓程序

开发 | 技术高人如何开发小程序?他们用这套方法

1062
来自专栏AndroidTv

AndroidTv Home界面实现原理(一)——Leanback 库的使用

接下去应该是梳理一下 Android Tv 主界面实现原理及解析的一个系列博客了,大体上的安排是先介绍 Google 官方提供的 Leanback 库的使用,如...

4377
来自专栏大前端开发

微信小程序视图层处理增强之WXS

随着微信开发者工具v1.0.0的释出,beta已久的微信小程序视图层的新功能特性WXS(WeiXin Script),也正式到来了。

712
来自专栏跬步

微信公众号迁移Serverless详解

3月腾讯云函数计算开放测试, 看到的第一反应是这种Serverless太适合做微信公众号的后端来实现自动应答了。

2533
来自专栏Aloys的开发之路

制作Aspose CHM文档的过程记录

欢迎和大家交流技术相关问题: 邮箱: jiangxinnju@163.com 博客园地址: http://www.cnblogs.com/jiangxinn...

1203

扫码关注云+社区

领取腾讯云代金券