iOS音頻(3):Audio File Stream(一)

Audio File Stream:它的作用是用來(lái)讀取采樣率向族、碼率搀继、時(shí)長(zhǎng)等基本信息以及分離音頻幀窘面。

音頻文件流服務(wù)支持以下音頻數(shù)據(jù)類型:
  • AIFF
  • AFIC
  • WAVE
  • CAF
  • NeXT
  • ADTS
  • MPEG Audio Layer 3
  • AAC

一、初始化AudioFileStream

OSStatus AudioFileStreamOpen(void *inClientData, 
AudioFileStream_PropertyListenerProc inPropertyListenerProc, 
AudioFileStream_PacketsProc inPacketsProc, AudioFileTypeID inFileTypeHint, 
AudioFileStreamID  _Nullable *outAudioFileStream);

參數(shù):
  • inClientData:指向要傳遞給回調(diào)函數(shù)的值或結(jié)構(gòu)的指針叽躯。
  • inPropertyListenerProc:您的屬性偵聽(tīng)器回調(diào)财边。只要解析器在數(shù)據(jù)流中找到屬性的值,就會(huì)使用屬性ID調(diào)用屬性偵聽(tīng)器点骑。然后您可以調(diào)用和函數(shù)來(lái)獲取該屬性的值酣难。AudioFileStreamGetPropertyInfoAudioFileStreamGetProperty
  • inPacketsProc:您的音頻數(shù)據(jù)回調(diào)谍夭。只要解析器在數(shù)據(jù)流中找到音頻數(shù)據(jù)包,它就會(huì)將數(shù)據(jù)傳遞給您的音頻數(shù)據(jù)回調(diào)憨募。
  • inFileTypeHint:音頻文件類型提示紧索。如果您打算傳遞給解析器的音頻文件流的類型是解析器無(wú)法輕松或唯一地根據(jù)數(shù)據(jù)(例如ADTS或AC3)確定的類型,則可以使用此參數(shù)指示類型菜谣。音頻文件服務(wù)中的枚舉中列出了可能的值齐板。AudioFileTypeID
    無(wú)法確定音頻文件類型,請(qǐng)通過(guò)0葛菇。
  • outAudioFileStream:在輸出上甘磨,表示音頻文件流解析器的不透明對(duì)象。該對(duì)象在本文檔中被稱為音頻文件流解析器ID眯停。您需要將此ID傳遞給音頻文件流API中的其他功能济舆。

AudioFileTypeID:

CF_ENUM(AudioFileTypeID) {
        kAudioFileAIFFType              = 'AIFF',
        kAudioFileAIFCType              = 'AIFC',
        kAudioFileWAVEType              = 'WAVE',
        kAudioFileRF64Type              = 'RF64',
        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',
        kAudioFileFLACType              = 'flac'
};

二、解析數(shù)據(jù)

獲取一些流式數(shù)據(jù)莺债。當(dāng)有數(shù)據(jù)傳遞給解析器時(shí)調(diào)用該函數(shù)滋觉。按順序?qū)?shù)據(jù)發(fā)送到解析器,如果可能的話齐邦,沒(méi)有間隙椎侠。

OSStatus AudioFileStreamParseBytes(
AudioFileStreamID inAudioFileStream, 
UInt32 inDataByteSize, 
const void *inData, 
AudioFileStreamParseFlags inFlags);
參數(shù)
  • inAudioFileStream:傳遞數(shù)據(jù)的解析器的ID。解析器ID由初始化時(shí)返回措拇。
  • inDataByteSize:本次解析的數(shù)據(jù)字節(jié)
  • inData:需要解析的數(shù)據(jù)
  • inFlags:本次的解析和上一次解析是否是連續(xù)的關(guān)系我纪,如果是連續(xù)的傳入0,不連續(xù)傳kAudioFileStreamParseFlag_Discontinuity

這里需要插入解釋一下何謂“連續(xù)”丐吓。在第一篇中我們提到過(guò)形如MP3的數(shù)據(jù)都以幀的形式存在的浅悉,解析時(shí)也需要以幀為單位解析。但在解碼之前我們不可能知道每個(gè)幀的邊界在第幾個(gè)字節(jié)券犁,所以就會(huì)出現(xiàn)這樣的情況:我們傳給AudioFileStreamParseBytes的數(shù)據(jù)在解析完成之后會(huì)有一部分?jǐn)?shù)據(jù)余下來(lái)术健,這部分?jǐn)?shù)據(jù)是接下去那一幀的前半部分,如果再次有數(shù)據(jù)輸入需要繼續(xù)解析時(shí)就必須要用到前一次解析余下來(lái)的數(shù)據(jù)才能保證幀數(shù)據(jù)完整粘衬,所以在正常播放的情況下傳入0即可荞估。目前知道的需要傳入kAudioFileStreamParseFlag_Discontinuity的情況有兩個(gè),一個(gè)是在seek完畢之后顯然seek后的數(shù)據(jù)和之前的數(shù)據(jù)完全無(wú)關(guān)稚新;
AudioFileStreamParseBytes方法的返回值表示當(dāng)前的數(shù)據(jù)是否被正常解析勘伺,如果OSStatus的值不是noErr則表示解析不成功,其中錯(cuò)誤碼包括:

enum
{
//指定的文件類型不受支持枷莉。
  kAudioFileStreamError_UnsupportedFileType        = 'typ?',
//數(shù)據(jù)格式不受指定文件類型的支持
  kAudioFileStreamError_UnsupportedDataFormat      = 'fmt?',
//該屬性不受支持
  kAudioFileStreamError_UnsupportedProperty        = 'pty?',
//為屬性數(shù)據(jù)提供的緩沖區(qū)大小不正確
  kAudioFileStreamError_BadPropertySize            = '!siz',
//由于流式音頻文件的數(shù)據(jù)包表或其他定義信息不存在或出現(xiàn)在音頻數(shù)據(jù)之后娇昙,因此不可能產(chǎn)生輸出數(shù)據(jù)包。
  kAudioFileStreamError_NotOptimized               = 'optm',
//數(shù)據(jù)包偏移小于0或超過(guò)文件末尾笤妙,或者在構(gòu)建數(shù)據(jù)包表時(shí)讀取損壞的數(shù)據(jù)包大小冒掌。
  kAudioFileStreamError_InvalidPacketOffset        = 'pck?',
//該文件格式不正確噪裕,不是其類型的音頻文件的有效實(shí)例,或者未被識(shí)別為音頻文件股毫。
  kAudioFileStreamError_InvalidFile                = 'dta?',
//音頻數(shù)據(jù)之前膳音,此文件中不存在屬性值。
  kAudioFileStreamError_ValueUnknown               = 'unk?',
//提供給解析器的數(shù)據(jù)量不足以產(chǎn)生任何結(jié)果铃诬。
  kAudioFileStreamError_DataUnavailable            = 'more',
//企圖進(jìn)行非法行動(dòng)祭陷。
  kAudioFileStreamError_IllegalOperation           = 'nope',
//發(fā)生未指定的錯(cuò)誤。
  kAudioFileStreamError_UnspecifiedError           = 'wht?',
//音頻數(shù)據(jù)中出現(xiàn)不連續(xù)性趣席,音頻文件流服務(wù)無(wú)法恢復(fù)兵志。
  kAudioFileStreamError_DiscontinuityCantRecover   = 'dsc!'
};

kAudioFileStreamError_NotOptimized:它的含義是說(shuō)這個(gè)音頻文件的文件頭不存在或者說(shuō)文件頭可能在文件的末尾,當(dāng)前無(wú)法正常Parse宣肚,換句話說(shuō)就是這個(gè)文件需要全部下載完才能播放想罕,無(wú)法流播。

注意:AudioFileStreamParseBytes方法每一次調(diào)用都應(yīng)該注意返回值霉涨,一旦出現(xiàn)錯(cuò)誤就可以不必繼續(xù)Parse了按价。

解析文件格式信息

在調(diào)用AudioFileStreamParseBytes方法進(jìn)行解析時(shí)會(huì)首先讀取格式信息,并同步的進(jìn)入AudioFileStream_PropertyListenerProc回調(diào)方法

typedef void (*AudioFileStream_PropertyListenerProc)(void *inClientData,
                                                     AudioFileStreamID inAudioFileStream, 
                                                     AudioFileStreamPropertyID inPropertyID, 
                                                     AudioFileStreamPropertyFlags *ioFlags);
參數(shù)
  • inClientData:Open方法中的上下文對(duì)象
  • inAudioFileStream:inAudioFileStream是和Open方法中第四個(gè)返回參數(shù)AudioFileStreamID一樣笙瑟,表示當(dāng)前FileStream的ID
  • inPropertyID:此次回調(diào)解析的信息ID楼镐。表示當(dāng)前PropertyID對(duì)應(yīng)的信息已經(jīng)解析完成信息(例如數(shù)據(jù)格式、音頻數(shù)據(jù)的偏移量等等)往枷,使用者可以通過(guò)AudioFileStreamGetProperty接口獲取PropertyID對(duì)應(yīng)的值或者數(shù)據(jù)結(jié)構(gòu)框产;
extern OSStatus AudioFileStreamGetProperty(AudioFileStreamID inAudioFileStream,
                                           AudioFileStreamPropertyID inPropertyID,
                                           UInt32 * ioPropertyDataSize,
                                           void * outPropertyData);
  • ioFlags:ioFlags是一個(gè)返回參數(shù),表示這個(gè)property是否需要被緩存师溅,如果需要賦值kAudioFileStreamPropertyFlag_PropertyIsCached否則不賦值(這個(gè)參數(shù)我也不知道應(yīng)該在啥場(chǎng)景下使用茅信。盾舌。墓臭。。)妖谴;

這個(gè)回調(diào)會(huì)進(jìn)來(lái)多次窿锉,但并不是每一次都需要進(jìn)行處理,可以根據(jù)需求處理需要的PropertyID進(jìn)行處理(PropertyID列表如下)膝舅。

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'
};

1嗡载、kAudioFileStreamProperty_BitRate:表示音頻數(shù)據(jù)的碼率,獲取這個(gè)Property是為了計(jì)算音頻的總時(shí)長(zhǎng)Duration(因?yàn)锳udioFileStream沒(méi)有這樣的接口仍稀。洼滚。)

UInt32 bitRate;
UInt32 bitRateSize = sizeof(bitRate);
OSStatus status = AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_BitRate, &bitRateSize, &bitRate);
if (status != noErr)
{
    //錯(cuò)誤處理
}

//在流播放的情況下,有時(shí)數(shù)據(jù)流量比較小時(shí)會(huì)出現(xiàn)ReadyToProducePackets還是沒(méi)有獲取到bitRate的情況技潘,這時(shí)就需要分離一些拼音幀然后計(jì)算平均bitRate遥巴,計(jì)算公式如下:
UInt32 averageBitRate = totalPackectByteCount / totalPacketCout;

2千康、kAudioFileStreamProperty_DataOffset:表示音頻數(shù)據(jù)在整個(gè)音頻文件中的offset(因?yàn)榇蠖鄶?shù)音頻文件都會(huì)有一個(gè)文件頭之后才使真正的音頻數(shù)據(jù)),這個(gè)值在seek時(shí)會(huì)發(fā)揮比較大的作用铲掐,音頻的seek并不是直接seek文件位置而seek時(shí)間(比如seek到2分10秒的位置)拾弃,seek時(shí)會(huì)根據(jù)時(shí)間計(jì)算出音頻數(shù)據(jù)的字節(jié)offset然后需要再加上音頻數(shù)據(jù)的offset才能得到在文件中的真正offset。

SInt64 dataOffset;
UInt32 offsetSize = sizeof(dataOffset);
OSStatus status = AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_DataOffset, &offsetSize, &dataOffset);
if (status != noErr)
{
    //錯(cuò)誤處理
}

3摆霉、kAudioFileStreamProperty_DataFormat:表示音頻文件結(jié)構(gòu)信息豪椿,是一個(gè)AudioStreamBasicDescription的結(jié)構(gòu)

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)
{
    //錯(cuò)誤處理
}

4、kAudioFileStreamProperty_FormatList:作用和kAudioFileStreamProperty_DataFormat是一樣的携栋,區(qū)別在于用這個(gè)PropertyID獲取到是一個(gè)AudioStreamBasicDescription的數(shù)組搭盾,這個(gè)參數(shù)是用來(lái)支持AAC SBR這樣的包含多個(gè)文件類型的音頻格式。由于到底有多少個(gè)format我們并不知曉婉支,所以需要先獲取一下總數(shù)據(jù)大小

//獲取數(shù)據(jù)大小
Boolean outWriteable;
UInt32 formatListSize;
OSStatus status = AudioFileStreamGetPropertyInfo(inAudioFileStream, kAudioFileStreamProperty_FormatList, &formatListSize, &outWriteable);
if (status != noErr)
{
    //錯(cuò)誤處理
}

//獲取formatlist
AudioFormatListItem *formatList = malloc(formatListSize);
OSStatus status = AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_FormatList, &formatListSize, formatList);
if (status != noErr)
{
    //錯(cuò)誤處理
}

//選擇需要的格式
for (int i = 0; i * sizeof(AudioFormatListItem) < formatListSize; i++)
{
    AudioStreamBasicDescription pasbd = formatList[i].mASBD;
    //選擇需要的格式增蹭。。                             
}
free(formatList);

5磅摹、kAudioFileStreamProperty_AudioDataByteCount:顧名思義滋迈,音頻文件中音頻數(shù)據(jù)的總量。這個(gè)Property的作用一是用來(lái)計(jì)算音頻的總時(shí)長(zhǎng)户誓,二是可以在seek時(shí)用來(lái)計(jì)算時(shí)間對(duì)應(yīng)的字節(jié)offset饼灿。

UInt64 audioDataByteCount;
UInt32 byteCountSize = sizeof(audioDataByteCount);
OSStatus status = AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_AudioDataByteCount, &byteCountSize, &audioDataByteCount);
if (status != noErr)
{
    //錯(cuò)誤處理
}
//有時(shí)數(shù)據(jù)流量比較小時(shí)會(huì)出現(xiàn)ReadyToProducePackets還是沒(méi)有獲取到audioDataByteCount的情況,這時(shí)就需要近似計(jì)算audioDataByteCount帝美。一般來(lái)說(shuō)音頻文件的總大小一定是可以得到的(利用文件系統(tǒng)或者Http請(qǐng)求中的contentLength)碍彭,那么計(jì)算方法如下:
UInt32 dataOffset = ...; //kAudioFileStreamProperty_DataOffset
UInt32 fileLength = ...; //音頻文件大小
UInt32 audioDataByteCount = fileLength - dataOffset;

6、kAudioFileStreamProperty_ReadyToProducePackets:這個(gè)PropertyID可以不必獲取對(duì)應(yīng)的值悼潭,一旦回調(diào)中這個(gè)PropertyID出現(xiàn)就代表解析完成庇忌,接下來(lái)可以對(duì)音頻數(shù)據(jù)進(jìn)行幀分離了。

計(jì)算時(shí)長(zhǎng)Duration

獲取時(shí)長(zhǎng)的最佳方法是從ID3信息中去讀取舰褪,那樣是最準(zhǔn)確的皆疹。如果ID3信息中沒(méi)有存,那就依賴于文件頭中的信息去計(jì)算了占拍。
double duration = (audioDataByteCount * 8) / bitRate
音頻數(shù)據(jù)的字節(jié)總量audioDataByteCount可以通過(guò)kAudioFileStreamProperty_AudioDataByteCount獲取略就,碼率bitRate可以通過(guò)kAudioFileStreamProperty_BitRate獲取也可以通過(guò)Parse一部分?jǐn)?shù)據(jù)后計(jì)算平均碼率來(lái)得到。
對(duì)于CBR數(shù)據(jù)來(lái)說(shuō)用這樣的計(jì)算方法的duration會(huì)比較準(zhǔn)確晃酒,對(duì)于VBR數(shù)據(jù)就不好說(shuō)了表牢。所以對(duì)于VBR數(shù)據(jù)來(lái)說(shuō),最好是能夠從ID3信息中獲取到duration贝次,獲取不到再想辦法通過(guò)計(jì)算平均碼率的途徑來(lái)計(jì)算duration崔兴。

分離音頻幀和關(guān)閉AudioFileStream下一篇再寫(xiě),這篇寫(xiě)的內(nèi)容有點(diǎn)多,消化消化~
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末敲茄,一起剝皮案震驚了整個(gè)濱河市螺戳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌折汞,老刑警劉巖倔幼,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異爽待,居然都是意外死亡损同,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)鸟款,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)膏燃,“玉大人,你說(shuō)我怎么就攤上這事何什∽榱ǎ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵处渣,是天一觀的道長(zhǎng)伶贰。 經(jīng)常有香客問(wèn)我,道長(zhǎng)罐栈,這世上最難降的妖魔是什么黍衙? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮荠诬,結(jié)果婚禮上琅翻,老公的妹妹穿的比我還像新娘。我一直安慰自己柑贞,他們只是感情好方椎,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著钧嘶,像睡著了一般棠众。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上康辑,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天摄欲,我揣著相機(jī)與錄音,去河邊找鬼疮薇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛我注,可吹牛的內(nèi)容都是我干的按咒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼励七!你這毒婦竟也來(lái)了智袭?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤掠抬,失蹤者是張志新(化名)和其女友劉穎吼野,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體两波,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瞳步,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了腰奋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片单起。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖劣坊,靈堂內(nèi)的尸體忽然破棺而出嘀倒,到底是詐尸還是另有隱情,我是刑警寧澤局冰,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布测蘑,位于F島的核電站,受9級(jí)特大地震影響康二,放射性物質(zhì)發(fā)生泄漏帮寻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一赠摇、第九天 我趴在偏房一處隱蔽的房頂上張望固逗。 院中可真熱鬧,春花似錦藕帜、人聲如沸烫罩。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)贝攒。三九已至,卻和暖如春时甚,著一層夾襖步出監(jiān)牢的瞬間隘弊,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工荒适, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留梨熙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓刀诬,卻偏偏與公主長(zhǎng)得像咽扇,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 音樂(lè)一直是我的愛(ài)好质欲,作為一名開(kāi)發(fā)树埠,同時(shí)我也想知道這些音樂(lè)是怎么播放的,音效是如何改變的嘶伟,如何升降調(diào)怎憋,一個(gè)音樂(lè)播放器...
    程序狗閱讀 5,336評(píng)論 1 8
  • AudioFileStream介紹 AudioFileStreamer時(shí)提到它的作用是用來(lái)讀取采樣率、碼率九昧、時(shí)長(zhǎng)等...
    VD2012閱讀 556評(píng)論 0 2
  • 本篇我們介紹AudioFile和AudioFileStream绊袋。在第一篇技術(shù)棧的分析里,我們提到過(guò)AudioFil...
    anyoptional閱讀 2,632評(píng)論 0 5
  • 教程一:視頻截圖(Tutorial 01: Making Screencaps) 首先我們需要了解視頻文件的一些基...
    90后的思維閱讀 4,697評(píng)論 0 3
  • 《ilua》速成開(kāi)發(fā)手冊(cè)3.0 官方用戶交流:iApp開(kāi)發(fā)交流(1) 239547050iApp開(kāi)發(fā)交流(2) 1...
    葉染柒丶閱讀 10,729評(píng)論 0 11