Objective-C - 将流数据传递到音频队列
Posted
技术标签:
【中文标题】Objective-C - 将流数据传递到音频队列【英文标题】:Objective-C - Passing Streamed Data to Audio Queue 【发布时间】:2012-05-14 12:04:21 【问题描述】:我目前正在 ios 上开发一个应用程序,它通过 TCP 套接字读取 IMA-ADPCM 音频数据并将其转换为 PCM,然后播放流。在这个阶段,我已经完成了从流中提取(或者我应该说对推送做出反应)数据并将其解码为 PCM 的类。我还设置了音频队列类并让它播放测试音。我需要帮助的地方是将数据传递到音频队列的最佳方式。
从 ADPCM 解码器输出的音频数据为 8 Khz 16 位 LPCM,每块 640 字节。 (它起源于 160 字节的 ADPCM 数据,但解压缩为 640)。它作为 uint_8t 数组进入函数并传递一个 NSData 对象。该流是“推送”流,因此每次发送音频时都会创建/刷新对象。
-(NSData*)convertADPCM:(uint8_t[]) adpcmdata
Audio Queue 回调当然是一个拉取函数,它在运行循环的每一轮中寻找数据,在它运行的每一轮中:
-(OSStatus) fillBuffer: (AudioQueueBufferRef) buffer
我已经为此工作了几天,PCM 转换非常费力,我在脑海中组装连接两者之间数据的最佳方式时遇到了一些麻烦。这不像我在创建数据,那么我可以简单地将数据创建合并到 fillbuffer 例程中,而不是推送数据。
我确实在 uint16_t[] 中设置了一个 0.5 秒的循环缓冲区〜但我认为我已经筋疲力尽,无法找到一种从缓冲区推拉的巧妙方法,所以我最终得到了啪啪啪啪啪。
我主要在 android 上完成了该项目,但发现 AudioTrack 与 Core-Audio Queues 完全不同。
在这个阶段,我还要说我拿起了 Adamson 和 Avila 所著的 Learning Core Audio 的副本,并发现对于任何想要揭开核心音频神秘面纱的人来说,这是一个极好的资源。
更新: 这是缓冲区管理代码:
-(OSStatus) fillBuffer: (AudioQueueBufferRef) buffer
int frame = 0;
double frameCount = bufferSize / self.streamFormat.mBytesPerFrame;
// buffersize = framecount = 8000 / 2 = 4000
//
// incoming buffer uint16_t[] convAudio holds 64400 bytes (big I know - 100 x 644 bytes)
// playedHead is set by the function to say where in the buffer the
// next starting point should be
if (playHead > 99)
playHead = 0;
// Playstep factors playhead to get starting position
int playStep = playHead * 644;
// filling the buffer
for (frame = 0; frame < frameCount; ++frame)
// framecount = 4000
// pointer to buffer
SInt16 *data = (SInt16*)buffer->mAudioData;
// load data from uint16_t[] convAudio array into frame
(data)[frame] = convAudio[(frame + playStep)];
// set buffersize
buffer->mAudioDataByteSize = bufferSize;
// return no Error - Osstatus will return an error otherwise if there is one. (I think)
return noErr;
正如我所说,当我写这篇文章时,我的大脑很模糊,我可能缺少一些明显的东西。
以上代码由回调调用:
static void MyAQOutputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inCompleteAQBuffer)
soundHandler *sHandler = (__bridge soundHandler*)inUserData;
CheckError([sHandler fillBuffer: inCompleteAQBuffer],
"can't refill buffer",
"buffer refilled");
CheckError(AudioQueueEnqueueBuffer(inAQ,
inCompleteAQBuffer,
0,
NULL),
"Couldn't enqueue buffer (refill)",
"buffer enqued (refill)");
在 convAudio 数组方面,我已将它转储到日志中,并且它正在以循环方式填充和重新填充,所以我知道至少那一点是有效的。
【问题讨论】:
发布您的缓冲区管理代码,因为这似乎是您的问题。 【参考方案1】:管理费率的难点,以及如果它们不匹配该怎么办。首先,尝试使用一个巨大的循环缓冲区(很多秒)并在启动音频队列之前将其填满。然后监视缓冲区级别以查看您遇到的速率匹配问题。
【讨论】:
以上是关于Objective-C - 将流数据传递到音频队列的主要内容,如果未能解决你的问题,请参考以下文章
将数据从 Swift 控制器传递到 Objective-C 控制器