1

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

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

AudioFileStreamParseBytes然后有两个回调函数: AudioFileStream_PropertyListenerProcAudioFileStream_PacketsProc。当在流中发现新属性和从流中接收到数据包时,分别调用这些家伙。收到数据包后,您可以使用音频队列服务将其提供给音频队列,该服务可以正常播放文件。

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

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

问题:
是否可以使用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 的数据限制。我很确定 900 字节就足够了(在场景 1 下测试时,每次总字节数为 417 并且工作正常)。

有任何想法吗?

4

1 回答 1

3

简短的回答是,让 AudioFileStreamParseBytes 解析音频数据包根本没有意义。在文档中AudioFileStreamParseBytes 是一个依赖于音频文件存在的函数(因此参数 inAudioFileStream.. 定义为您希望向其传递数据的解析器的 ID。解析器 ID 由 AudioFileStreamOpen 函数返回。

所以吸取的教训:不要试图将iOS功能归类到适合你的情况..它应该是相反的。

我最终做的是将数据直接提供给音频队列..无需通过所有这些不必要的中间功能..更深入的方式是将数据提供给音频单元..但我的应用程序不需要那个级别控制的

于 2012-11-22T13:58:28.807 回答