第三部分转载自:
码农人生
前言
To play streamed audio content, such as from a network connection, use Audio File Stream Services in concert with Audio Queue Services. Audio File Stream Services parses audio packets and metadata from common audio file container formats in a network bitstream. You can also use it to parse packets and metadata from on-disk files |
- extern OSStatus AudioFileStreamOpen (void * inClientData,
- AudioFileStream_PropertyListenerProc inPropertyListenerProc,
- AudioFileStream_PacketsProc inPacketsProc,
- AudioFileTypeID inFileTypeHint,
- AudioFileStreamID * outAudioFileStream);
- //AudioFileTypeID枚举
- enum {
- kAudioFileAIFFType = 'AIFF',
- kAudioFileAIFCType = 'AIFC',
- kAudioFileWAVEType = 'WAVE',
- kAudioFileSoundDesigner2Type = 'Sd2f',
- kAudioFileNextType = 'NeXT',
- kAudioFileMP3Type = 'MPG3', // mpeg layer 3
- kAudioFileMP2Type = 'MPG2', // mpeg layer 2
- kAudioFileMP1Type = 'MPG1', // mpeg layer 1
- kAudioFileAC3Type = 'ac-3',
- kAudioFileAAC_ADTSType = 'adts',
- kAudioFileMPEG4Type = 'mp4f',
- kAudioFileM4AType = 'm4af',
- kAudioFileM4BType = 'm4bf',
- kAudioFileCAFType = 'caff',
- kAudioFile3GPType = '3gpp',
- kAudioFile3GP2Type = '3gp2',
- kAudioFileAMRType = 'amrf'
- };
- extern OSStatus AudioFileStreamParseBytes(AudioFileStreamID inAudioFileStream,
- UInt32 inDataByteSize,
- const void* inData,
- UInt32 inFlags);
the Audio File Stream Services hit me with a nasty bug:AudioFileStreamParseBytes will crash when trying to parse a streaming MP3.In this case, if we pass the kAudioFileStreamParseFlag_Discontinuity flag to AudioFileStreamParseBytes on every invocation between receiving kAudioFileStreamProperty_ReadyToProducePackets and the first successful call to MyPacketsProc, then AudioFileStreamParseBytes will be extra cautious in its approach and won't crash. |
- enum
- {
- kAudioFileStreamError_UnsupportedFileType = 'typ?',
- kAudioFileStreamError_UnsupportedDataFormat = 'fmt?',
- kAudioFileStreamError_UnsupportedProperty = 'pty?',
- kAudioFileStreamError_BadPropertySize = '!siz',
- kAudioFileStreamError_NotOptimized = 'optm',
- kAudioFileStreamError_InvalidPacketOffset = 'pck?',
- kAudioFileStreamError_InvalidFile = 'dta?',
- kAudioFileStreamError_ValueUnknown = 'unk?',
- kAudioFileStreamError_DataUnavailable = 'more',
- kAudioFileStreamError_IllegalOperation = 'nope',
- kAudioFileStreamError_UnspecifiedError = 'wht?',
- kAudioFileStreamError_DiscontinuityCantRecover = 'dsc!'
- };
It is not possible to produce output packets because the file's packet table or other defining info is either not present or is after the audio data. |
- typedef void (*AudioFileStream_PropertyListenerProc)(void * inClientData,
- AudioFileStreamID inAudioFileStream,
- AudioFileStreamPropertyID inPropertyID,
- UInt32 * ioFlags);
- extern OSStatus AudioFileStreamGetProperty(AudioFileStreamID inAudioFileStream,
- AudioFileStreamPropertyID inPropertyID,
- UInt32 * ioPropertyDataSize,
- void * outPropertyData);
- //AudioFileStreamProperty枚举
- enum
- {
- kAudioFileStreamProperty_ReadyToProducePackets = 'redy',
- kAudioFileStreamProperty_FileFormat = 'ffmt',
- kAudioFileStreamProperty_DataFormat = 'dfmt',
- kAudioFileStreamProperty_FormatList = 'flst',
- kAudioFileStreamProperty_MagicCookieData = 'mgic',
- kAudioFileStreamProperty_AudioDataByteCount = 'bcnt',
- kAudioFileStreamProperty_AudioDataPacketCount = 'pcnt',
- kAudioFileStreamProperty_MaximumPacketSize = 'psze',
- kAudioFileStreamProperty_DataOffset = 'doff',
- kAudioFileStreamProperty_ChannelLayout = 'cmap',
- kAudioFileStreamProperty_PacketToFrame = 'pkfr',
- kAudioFileStreamProperty_FrameToPacket = 'frpk',
- kAudioFileStreamProperty_PacketToByte = 'pkby',
- kAudioFileStreamProperty_ByteToPacket = 'bypk',
- kAudioFileStreamProperty_PacketTableInfo = 'pnfo',
- kAudioFileStreamProperty_PacketSizeUpperBound = 'pkub',
- kAudioFileStreamProperty_AverageBytesPerPacket = 'abpp',
- kAudioFileStreamProperty_BitRate = 'brat',
- kAudioFileStreamProperty_InfoDictionary = 'info'
- };
- UInt32 bitRate;
- UInt32 bitRateSize = sizeof(bitRate);
- OSStatus status = AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_BitRate, &bitRateSize, &bitRate);
- if (status != noErr)
- {
- //错误处理
- }
- SInt64 dataOffset;
- UInt32 offsetSize = sizeof(dataOffset);
- OSStatus status = AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_DataOffset, &offsetSize, &dataOffset);
- if (status != noErr)
- {
- //错误处理
- }
- struct AudioStreamBasicDescription
- {
- Float64 mSampleRate;
- UInt32 mFormatID;
- UInt32 mFormatFlags;
- UInt32 mBytesPerPacket;
- UInt32 mFramesPerPacket;
- UInt32 mBytesPerFrame;
- UInt32 mChannelsPerFrame;
- UInt32 mBitsPerChannel;
- UInt32 mReserved;
- };
- AudioStreamBasicDescription asbd;
- UInt32 asbdSize = sizeof(asbd);
- OSStatus status = AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_DataFormat, &asbdSize, &asbd);
- if (status != noErr)
- {
- //错误处理
- }
- //获取数据大小
- Boolean outWriteable;
- UInt32 formatListSize;
- OSStatus status = AudioFileStreamGetPropertyInfo(inAudioFileStream, kAudioFileStreamProperty_FormatList, &formatListSize, &outWriteable);
- if (status != noErr)
- {
- //错误处理
- }
- //获取formatlist
- AudioFormatListItem *formatList = malloc(formatListSize);
- OSStatus status = AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_FormatList, &formatListSize, formatList);
- if (status != noErr)
- {
- //错误处理
- }
- //选择需要的格式
- for (int i = 0; i * sizeof(AudioFormatListItem) < formatListSize; i += sizeof(AudioFormatListItem))
- {
- AudioStreamBasicDescription pasbd = formatList[i].mASBD;
- //选择需要的格式。。
- }
- free(formatList);
- UInt64 audioDataByteCount;
- UInt32 byteCountSize = sizeof(audioDataByteCount);
- OSStatus status = AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_AudioDataByteCount, &byteCountSize, &audioDataByteCount);
- if (status != noErr)
- {
- //错误处理
- }
- double duration = (audioDataByteCount * 8) / bitRate
- typedef void (*AudioFileStream_PacketsProc)(void * inClientData,
- UInt32 inNumberBytes,
- UInt32 inNumberPackets,
- const void * inInputData,
- AudioStreamPacketDescription * inPacketDescriptions);
- //AudioStreamPacketDescription结构
- //这里的mVariableFramesInPacket是指实际的数据帧只有VBR的数据才能用到(像MP3这样的压缩数据一个帧里会有好几个数据帧)
- struct AudioStreamPacketDescription
- {
- SInt64 mStartOffset;
- UInt32 mVariableFramesInPacket;
- UInt32 mDataByteSize;
- };
- static void MyAudioFileStreamPacketsCallBack(void *inClientData,
- UInt32 inNumberBytes,
- UInt32 inNumberPackets,
- const void *inInputData,
- AudioStreamPacketDescription *inPacketDescriptions)
- {
- //处理discontinuous..
- if (numberOfBytes == 0 || numberOfPackets == 0)
- {
- return;
- }
- BOOL deletePackDesc = NO;
- if (packetDescriptioins == NULL)
- {
- //如果packetDescriptioins不存在,就按照CBR处理,平均每一帧的数据后生成packetDescriptioins
- deletePackDesc = YES;
- UInt32 packetSize = numberOfBytes / numberOfPackets;
- packetDescriptioins = (AudioStreamPacketDescription *)malloc(sizeof(AudioStreamPacketDescription) * numberOfPackets);
- for (int i = 0; i < numberOfPackets; i++)
- {
- UInt32 packetOffset = packetSize * i;
- descriptions[i].mStartOffset = packetOffset;
- descriptions[i].mVariableFramesInPacket = 0;
- if (i == numberOfPackets - 1)
- {
- packetDescriptioins[i].mDataByteSize = numberOfBytes - packetOffset;
- }
- else
- {
- packetDescriptioins[i].mDataByteSize = packetSize;
- }
- }
- }
- for (int i = 0; i < numberOfPackets; ++i)
- {
- SInt64 packetOffset = packetDescriptioins[i].mStartOffset;
- UInt32 packetSize = packetDescriptioins[i].mDataByteSize;
- //把解析出来的帧数据放进自己的buffer中
- ...
- }
- if (deletePackDesc)
- {
- free(packetDescriptioins);
- }
- }
- double seekToTime = ...; //需要seek到哪个时间,秒为单位
- UInt64 audioDataByteCount = ...; //通过kAudioFileStreamProperty_AudioDataByteCount获取的值
- SInt64 dataOffset = ...; //通过kAudioFileStreamProperty_DataOffset获取的值
- double durtion = ...; //通过公式(AudioDataByteCount * 8) / BitRate计算得到的时长
- //近似seekOffset = 数据偏移 + seekToTime对应的近似字节数
- SInt64 approximateSeekOffset = dataOffset + (seekToTime / duration) * audioDataByteCount;
- //首先需要计算每个packet对应的时长
- AudioStreamBasicDescription asbd = ...; ////通过kAudioFileStreamProperty_DataFormat或者kAudioFileStreamProperty_FormatList获取的值
- double packetDuration = asbd.mFramesPerPacket / asbd.mSampleRate
- //然后计算packet位置
- SInt64 seekToPacket = floor(seekToTime / packetDuration);
- SInt64 seekByteOffset;
- UInt32 ioFlags = 0;
- SInt64 outDataByteOffset;
- OSStatus status = AudioFileStreamSeek(audioFileStreamID, seekToPacket, &outDataByteOffset, &ioFlags);
- if (status == noErr && !(ioFlags & kAudioFileStreamSeekFlag_OffsetIsEstimated))
- {
- //如果AudioFileStreamSeek方法找到了帧的字节偏移,需要修正一下时间
- seekToTime -= ((seekByteOffset - dataOffset) - outDataByteOffset) * 8.0 / bitRate;
- seekByteOffset = outDataByteOffset + dataOffset;
- }
- else
- {
- seekByteOffset = approximateSeekOffset;
- }
- extern OSStatus AudioFileStreamClose(AudioFileStreamID inAudioFileStream);