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 - 将流数据传递到音频队列的主要内容,如果未能解决你的问题,请参考以下文章

在 Objective-c 中使用调度队列

将流写入缓冲区对象

将数据从 Swift 控制器传递到 Objective-C 控制器

从 sqlite 数据库 Objective-C 播放音频文件

Objective-C 流式上传音频

如何将流 responseType 传递给我的客户端?