AVAssetReader和AudioFileReadPackets如何读取音频之间的差异

时间:2012-09-08 08:29:52

标签: ios audio streaming

考虑从音频文件读取/写入数据的这两种情况(用于通过网络发送):

场景1:音频文件服务:
使用Audio File Services中的 AudioFileReadPackets 。这会生成您可以通过网络轻松发送的音频数据包。在接收方,您使用 AudioFileStreamOpen AudioFileStreamParseBytes 来解析数据。

AudioFileStreamParseBytes 则有两个回调函数:AudioFileStream_PropertyListenerProcAudioFileStream_PacketsProc。当在流中发现新属性并且分别从流接收到分组时,将调用这些人。收到数据包后,您可以使用Audio Queue Service将其提供给音频队列,播放文件就可以了。

注意:此方法不适用于存储在iPod库中的音乐文件,这使我们进入第二种情况:

场景2:AVAssetReader:
使用AVAssetReader,您可以从iPod音乐库中读取并通过网络发送数据包。通常,您可以直接在类似于上面的音频队列上加载数据包。但是,在这种情况下,您必须创建一个线程以确保在队列已满时阻止接收数据包,并在队列缓冲区可用时取消阻止(请参阅this示例)。

问题:
是否可以使用 AVAssetReader 发送数据包,只是让它通过 AudioFileStreamParseBytes 读取? (动机是AudioFileStreamParseBytes的回调将处理线程/阻塞业务并为您节省痛苦)。我试着这样做:
1。首先使用AVAssetReader

读取音频文件
//NSURL *assetURL = [NSURL URLWithString:@"ipod-library://item/item.m4a?id=1053020204400037178"]; 
AVURLAsset *songAsset = [AVURLAsset URLAssetWithURL:assetURL options:nil];

NSError * error = nil;
AVAssetReader* reader = [[AVAssetReader alloc] initWithAsset:songAsset error:&error];

AVAssetTrack* track = [songAsset.tracks objectAtIndex:0]; 

// Note: I don't supply an audio format description here, rather I pass on nil to keep the original
// file format. In another piece of code (see here: http://stackoverflow.com/questions/12264799/why-is-audio-coming-up-garbled-when-using-avassetreader-with-audio-queue?answertab=active#tab-top) I can extract the audio format from the track, let's say it's an AAC format.
AVAssetReaderTrackOutput* readerOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:track
                                                                                    outputSettings:nil];


[reader addOutput:readerOutput];
[reader startReading];

2。设置流光

// notice how i manually add the audio file type (for the file hint parameter) 
// using the info from step one.. If i leave it as 0, this call fails and returns
// the typ? error, which is :The specified file type is not supported.  
streamer->err = AudioFileStreamOpen((__bridge void*)streamer, 
                                     ASPropertyListenerProc, ASPacketsProc, 
                                     kAudioFileAAC_ADTSType, &(streamer->audioFileStream)); 

3。收到数据后,我解析字节:

streamer->err = AudioFileStreamParseBytes(streamer->audioFileStream, inDataByteSize, inData, 0);

问题:当我这样做时..我发送字节而AudioFileStreamParseBytes 失败。但是,永远不会调用回调* AudioFileStream_PropertyListenerProc *和* AudioFileStream_PacketsProc *。这让我觉得解析器无法解析字节并从中提取任何有用的信息..在AudioStreamParseBytes的文档中它指出:*你应该提供至少多个数据包的音频文件数据,但最好一次提供几个数据包到几秒钟的数据。*我发送超过900个字节,这正好低于GKSession的数据limit。我很确定900字节是足够的(在方案1下测试时,每次总字节数为417,并且工作正常)。

有什么想法吗?

1 个答案:

答案 0 :(得分:3)

简短的回答是,音频数据包被AudioFileStreamParseBytes解析是没有意义的.. docs AudioFileStreamParseBytes是一个依赖于音频文件存在的函数(因此参数inAudioFileStream ..它被定义为你希望传递数据的解析器的ID。解析器ID由AudioFileStreamOpen函数返回。

所以吸取了教训:不要试图通过iOS功能来适应你的情况......它应该是另一种方式。

我最终做的是将数据直接输入音频队列..而不经过所有这些不必要的中间功能..更深入的方式是将数据馈送到音频单元..但我的应用程序没有需要那种控制水平