iOS 音頻處理框架及重點(diǎn) API 合集丨音視頻工程示例

畢加索《手里捧著鴿子的孩子》像素版

iOS/Android 客戶端開發(fā)同學(xué)如果想要開始學(xué)習(xí)音視頻開發(fā)侦讨,最絲滑的方式是對(duì)音視頻基礎(chǔ)概念知識(shí)有一定了解后,再借助 iOS/Android 平臺(tái)的音視頻能力上手去實(shí)踐音視頻的采集 → 編碼 → 封裝 → 解封裝 → 解碼 → 渲染過程钦奋,并借助音視頻工具來分析和理解對(duì)應(yīng)的音視頻數(shù)據(jù)衰倦。

音視頻工程示例這個(gè)欄目的前面 6 篇 AVDemo 文章中袒炉,我們拆解了音頻的采集 → 編碼 → 封裝 → 解封裝 → 解碼 → 渲染流程并基于 iOS 系統(tǒng) API 實(shí)現(xiàn)了 Demo:

iOS AVDemo(1):音頻采集

iOS AVDemo(2):音頻編碼

iOS AVDemo(3):音頻封裝

iOS AVDemo(4):音頻解封裝

iOS AVDemo(5):音頻解碼

iOS AVDemo(6):音頻渲染

你可以在關(guān)注本公眾號(hào)后,在公眾號(hào)發(fā)送消息『AVDemo』來獲取 Demo 的全部源碼樊零。

如果你看完這些 Demo梳杏,對(duì) iOS 平臺(tái)的音視頻開發(fā)多多少少會(huì)有一些認(rèn)識(shí)了,在這個(gè)基礎(chǔ)上我們來總結(jié)一下 iOS 音頻處理框架淹接,以及在前面的 Demo 中我們用到的主要 API 和數(shù)據(jù)結(jié)構(gòu)有哪些。

1叛溢、iOS 音頻框架

當(dāng)我們想要了解 iOS 的音頻處理框架時(shí)塑悼,以下是我們能比較容易找到的兩張官方架構(gòu)圖。它們分別出自Audio Unit Hosting Guide for iOS[1]Core Audio Overview[2]這兩篇文檔楷掉。

iOS Audio Frameworks

Core Audio API Layers

但這兩篇文檔已經(jīng)比較陳舊了厢蒜,是多年之前的文檔,以至于和最新的 iOS 15 的框架有不少的出入烹植。而新版本的 iOS 官方技術(shù)文檔也沒有給出比較清晰的音頻架構(gòu)圖斑鸦。所以在這里我們就按照 Demo 中涉及的系統(tǒng) API 來挑選介紹幾個(gè)相關(guān)的 Framework:

Audio Unit Framework

Core Media Framework

Audio Toolbox Framework

AVFoundation Framework

2、Audio Unit Framework

Audio Unit Framework[3]:最底層的音頻處理 API草雕,功能強(qiáng)大巷屿,直接驅(qū)動(dòng)底層硬件,提供快速墩虹、模塊化的音頻處理嘱巾。當(dāng)你要實(shí)現(xiàn)低延遲的音頻處理(比如 VoIP)、對(duì)合成聲音進(jìn)行響應(yīng)式的播放(比如音樂游戲诫钓、合成樂器聲音)旬昭、實(shí)現(xiàn)特定的音頻能力(比如回聲消除、混音菌湃、聲音均衡)问拘、實(shí)現(xiàn)音頻處理鏈支持靈活組裝音頻處理單元時(shí),你可以選擇使用 Audio Unit 的 API。

需要注意的是骤坐,在最新的 iOS 系統(tǒng)庫(kù)架構(gòu)中绪杏,Audio Unit Framework 的實(shí)現(xiàn)都已經(jīng)遷移到 Audio Toolbox Framework 中去了。

下面是 Audio Unit 框架的主要模塊:

1)Audio Component Services[4]:定義了發(fā)現(xiàn)或油、開啟和關(guān)閉音頻單元(audio unit)以及音頻編解碼器的接口寞忿。

常用的數(shù)據(jù)類型:

AudioComponent[5]:表示音頻組件。一種音頻組件通常由 type顶岸、subtype腔彰、manufacturer 三屬性來唯一標(biāo)識(shí)。

AudioComponentDescription[6]:表示音頻組件的描述辖佣。其中 type霹抛、subtype、manufacturer 三屬性組合起來標(biāo)識(shí)一種音頻組件卷谈。

AudioComponentInstance[7]:表示音頻組件的實(shí)例杯拐。通常我們使用查找方法(AudioComponentFindNext)來找到符合描述的音頻組件,然后再去使用創(chuàng)建方法(AudioComponentInstanceNew)創(chuàng)建一個(gè)對(duì)應(yīng)的音頻組件實(shí)例世蔗。

常用的接口:

AudioComponentFindNext(...)[8]:用于查找符合描述的音頻組件端逼。

AudioComponentGetDescription(...)[9]:用于獲取一種音頻組件對(duì)應(yīng)的描述。

AudioComponentInstanceNew(...)[10]:創(chuàng)建一個(gè)音頻組件實(shí)例污淋。

AudioComponentInstanceDispose(...)[11]:釋放一個(gè)音頻組件實(shí)例顶滩。

2)Audio Unit Component Services[12]:提供了使用音頻單元(audio unit)的 C 語(yǔ)言接口。一個(gè)音頻單元(audio unit)是用來進(jìn)行音頻數(shù)據(jù)處理或者音頻數(shù)據(jù)生成的插件單元寸爆。要發(fā)現(xiàn)礁鲁、開啟、關(guān)閉音頻單元(audio unit)則可以使用 Audio Component Services赁豆。

常用的數(shù)據(jù)類型:

AudioUnit[13]仅醇,typedef AudioComponentInstance AudioUnit;。AudioUnit 就是一種 AudioComponentInstance魔种。

AudioUnitParameter[14]:表示 AudioUnit 的參數(shù)析二,一個(gè) AudioUnit 參數(shù)由 scope、element节预、parameterID 三屬性定義甲抖。

AudioUnitProperty[15]:表示 AudioUnit 的屬性,一個(gè) AudioUnitProperty 由 scope心铃、element准谚、propertyID 三屬性定義。

常用的接口:

AudioUnitInitialize(...)[16]:初始化一個(gè) AudioUnit去扣。如果初始化成功柱衔,說明 input/output 的格式是可支持的樊破,并且處于可以開始渲染的狀態(tài)。

AudioUnitUninitialize(...)[17]:卸載一個(gè) AudioUnit唆铐。一旦一個(gè) AudioUnit 被初始化后哲戚,要想改變它的狀態(tài)來響應(yīng)某些環(huán)境變化,就需要先卸載艾岂。這時(shí)候會(huì)使得 AudioUnit 釋放它的資源顺少。此后,調(diào)用者可以重新配置這個(gè) AudioUnit 來適配新的環(huán)境王浴,比如處理與之前不同的采樣率脆炎。在這之后,可以重新初始化這個(gè) AudioUnit 來應(yīng)用這些更改氓辣。

AudioUnitRender(...)[18]:渲染輸入的音頻數(shù)據(jù)秒裕,數(shù)據(jù)量為 inNumberOfFrames。渲染結(jié)果的數(shù)據(jù)存儲(chǔ)在 ioData 中钞啸。注意調(diào)用方需要提供音頻時(shí)間戳几蜻,這個(gè)時(shí)間戳應(yīng)該滿足單調(diào)遞增。

AudioUnitGetProperty(...)[19]:獲取 AudioUnit 的屬性体斩。

AudioUnitSetProperty(...)[20]:設(shè)置 AudioUnit 的屬性梭稚。

AudioUnitGetParameter(...)[21]:獲取 AudioUnit 的參數(shù)。

AudioUnitSetParameter(...)[22]:設(shè)置 AudioUnit 的參數(shù)絮吵。

常用的回調(diào):

AURenderCallback[23]:在以下幾種情況會(huì)被系統(tǒng)調(diào)用:當(dāng) AudioUnit 需要輸入采樣數(shù)據(jù)弧烤;在一個(gè)渲染操作前;在一個(gè)渲染操作后源武。

AudioUnitPropertyListenerProc[24]:當(dāng)一個(gè)指定的 AudioUnit 的 property 發(fā)生改變時(shí),會(huì)被系統(tǒng)調(diào)用想幻。

3)Output Audio Unit Services[25]:提供了 start粱栖、stop 用于 I/O 的音頻單元(通常是用于輸出的音頻單元)的 C 語(yǔ)言接口。

常用的接口:

AudioOutputUnitStart(...)[26]:?jiǎn)?dòng)一個(gè) I/O AudioUnit脏毯,同時(shí)會(huì)啟動(dòng)與之連接的 AudioUnit Processing Graph闹究。

AudioOutputUnitStop(...)[27]:關(guān)閉一個(gè) I/O AudioUnit,同時(shí)會(huì)關(guān)閉與之連接的 AudioUnit Processing Graph食店。

3渣淤、Core Media Framework

Core Media Framework[28]:定義和封裝了 AVFoundation 等更上層的媒體框架需要的媒體處理流水線(包含時(shí)間信息)以及其中使用的接口和數(shù)據(jù)類型。使用 Core Media 層的接口和數(shù)據(jù)類型可以高效的處理媒體采樣數(shù)據(jù)吉嫩、管理采樣數(shù)據(jù)隊(duì)列价认。下面是 Core Media 框架的主要模塊:

1)Sample Processing[29]:采樣數(shù)據(jù)處理。常用的數(shù)據(jù)類型:

CMSampleBuffer[30]:系統(tǒng)用來在音視頻處理的 pipeline 中使用和傳遞媒體采樣數(shù)據(jù)的核心數(shù)據(jù)結(jié)構(gòu)自娩。你可以認(rèn)為它是 iOS 音視頻處理 pipeline 中的流通貨幣用踩,攝像頭采集的視頻數(shù)據(jù)接口、麥克風(fēng)采集的音頻數(shù)據(jù)接口、編碼和解碼數(shù)據(jù)接口脐彩、讀取和存儲(chǔ)視頻接口碎乃、視頻渲染接口等等,都以它作為參數(shù)惠奸。通常梅誓,CMSampleBuffer 中要么包含一個(gè)或多個(gè)媒體采樣的 CMBlockBuffer,要么包含一個(gè) CVImageBuffer佛南。

CMSampleBufferCreateReady(...)[31]:基于媒體數(shù)據(jù)創(chuàng)建一個(gè) CMSampleBuffer梗掰。

CMSampleBufferCreate(...)[32]:創(chuàng)建一個(gè) CMSampleBuffer,支持設(shè)置數(shù)據(jù)已準(zhǔn)備好的回調(diào)共虑。

CMSampleBufferSetDataBufferFromAudioBufferList(...)[33]:為指定的 CMSampleBuffer 創(chuàng)建其對(duì)應(yīng)的 CMBlockBuffer愧怜,其中的數(shù)據(jù)拷貝自 AudioBufferList。

CMSampleBufferGetFormatDescription(...)[34]:返回 CMSampleBuffer 中的采樣數(shù)據(jù)對(duì)應(yīng)的 CMFormatDescription妈拌。

CMSampleBufferGetDataBuffer(...)[35]:返回 CMSampleBuffer 中的 CMBlockBuffer拥坛。注意調(diào)用方不會(huì)持有返回的 CMBlockBuffer,如果想要維護(hù)指向它的指針尘分,需要顯式 retain 一下猜惋。

CMSampleBufferGetPresentationTimeStamp(...)[36]:獲取 CMSampleBuffer 中所有采樣的最小的 pts 時(shí)間戳。

CMBlockBuffer[37]:一個(gè)或多個(gè)媒體采樣的的裸數(shù)據(jù)培愁。其中可以封裝:音頻采集后著摔、編碼后、解碼后的數(shù)據(jù)(如:PCM 數(shù)據(jù)定续、AAC 數(shù)據(jù))谍咆;視頻編碼后的數(shù)據(jù)(如:H.264 數(shù)據(jù))。

CMBlockBufferCreateWithMemoryBlock(...)[38]:基于內(nèi)存數(shù)據(jù)創(chuàng)建一個(gè) CMBlockBuffer私股。

CMBlockBufferGetDataPointer(...)[39]:獲取訪問 CMBlockBuffer 中數(shù)據(jù)的地址摹察。

CMFormatDescription[40]:用于描述 CMSampleBuffer 中采樣的格式信息。

CMFormatDescriptionCreate(...)[41]:創(chuàng)建一個(gè) CMFormatDescription倡鲸。

CMAudioFormatDescription[42]:typedef CMFormatDescriptionRef CMAudioFormatDescriptionRef;供嚎。CMAudioFormatDescription 是一種 CMFormatDescriptionRef。

CMAudioFormatDescriptionCreate(...)[43]:基于 AudioStreamBasicDescription 來創(chuàng)建一個(gè) CMAudioFormatDescription峭状。

CMAudioFormatDescriptionGetStreamBasicDescription(...)[44]:返回一個(gè)指向 CMFormatDescription(通常應(yīng)該是一個(gè) CMAudioFormatDescription) 中的 AudioStreamBasicDescription 的指針克滴。如果是非音頻格式,就返回 NULL优床。

CMAttachment[45]:為 CMSampleBuffer 添加支持的 metadata劝赔。

這里我們還要補(bǔ)充介紹 CoreAudioTypes Framework 中的幾種數(shù)據(jù)類型:

AudioStreamBasicDescription[46]:用于描述音頻流數(shù)據(jù)格式信息,比如采樣位深胆敞、聲道數(shù)望忆、采樣率罩阵、每幀字節(jié)數(shù)、每包幀數(shù)启摄、每包字節(jié)數(shù)稿壁、格式標(biāo)識(shí)等。

AudioBuffer[47]:存儲(chǔ)并描述音頻數(shù)據(jù)的緩沖區(qū)歉备。mData 中存儲(chǔ)著數(shù)據(jù)傅是。可以存儲(chǔ)兩種不同類型的音頻:1)單聲道音頻數(shù)據(jù)蕾羊;2)多聲道的交錯(cuò)音頻數(shù)據(jù)喧笔,這時(shí)候 mNumberChannels 指定了聲道數(shù)。

AudioBufferList[48]:一組 AudioBuffer龟再。

AudioTimeStamp[49]:從多維度來表示一個(gè)時(shí)間戳的數(shù)據(jù)結(jié)構(gòu)书闸。

2)Time Representation[50]:時(shí)間信息表示。常用的數(shù)據(jù)類型:

CMTime[51]:用 value/timescale 的方式表示時(shí)間利凑。這樣可以解決浮點(diǎn)運(yùn)算時(shí)的精度損失問題浆劲。timescale 表示時(shí)間刻度,通常在處理視頻內(nèi)容時(shí)常見的時(shí)間刻度為 600哀澈,這是大部分常用視頻幀率 24fps牌借、25fps、30fps 的公倍數(shù)割按,音頻數(shù)據(jù)常見的時(shí)間刻度就是采樣率膨报,比如 44100 或 48000。

CMTimeRange[52]:用 start+duration 的方式表示一段時(shí)間适荣。

CMSampleTimingInfo[53]:一個(gè) CMSampleBuffer 的時(shí)間戳信息现柠,包括 pts、dts弛矛、duration够吩。

3)Queues[54]:數(shù)據(jù)容器。常用的數(shù)據(jù)類型:

CMSimpleQueue[55]:一個(gè)簡(jiǎn)單地汪诉、無鎖的 FIFO 隊(duì)列废恋,可以放(void *)元素谈秫,元素不能是 NULL 或 0扒寄,如果元素是指向分配內(nèi)存的指針,其內(nèi)存生命周期要在外面自己管理拟烫「帽啵可以用作音視頻采樣數(shù)據(jù)(CMSampleBufferRef)的隊(duì)列,不過要自己加鎖硕淑。

CMBufferQueue[56]:支持存儲(chǔ)任何 CFTypeRef 類型的數(shù)據(jù)课竣,但是數(shù)據(jù)類型需要有 duration 的概念嘉赎,在創(chuàng)建 CMBufferQueue 的時(shí)候,會(huì)有一些回調(diào)于樟,其中一個(gè)必須的回調(diào)是要返回隊(duì)列中對(duì)象的 duration公条。CMBufferQueue 是設(shè)計(jì)用于在生產(chǎn)者/消費(fèi)者模型中在不同的線程中讀寫數(shù)據(jù)。通常是兩個(gè)線程(一個(gè)是生產(chǎn)者入隊(duì)線程迂曲,一個(gè)是消費(fèi)者出隊(duì)線程)靶橱,當(dāng)然更多的線程也是可以的。

CMMemoryPool[57]:內(nèi)存池容器路捧,對(duì)使用大塊的內(nèi)存有優(yōu)化关霸。一個(gè) CMMemoryPool 的實(shí)例實(shí)際上維護(hù)一個(gè)最近釋放內(nèi)存的池子用于內(nèi)存分配服務(wù)。這樣的目的是加快隨后的內(nèi)存分配杰扫。在需要重復(fù)分配大塊內(nèi)存時(shí)队寇,比如輸出視頻編碼數(shù)據(jù),可以使用這個(gè)數(shù)據(jù)結(jié)構(gòu)章姓。

4佳遣、Audio Toolbox Framework

Audio Toolbox Framework[58]:提供了音頻錄制、播放啤覆、流解析苍日、編碼格式轉(zhuǎn)換、Audio Session 管理等功能接口窗声。下面是 Audio Toolbox 框架的主要模塊:

1)Audio Units[59]:音頻單元相恃。關(guān)于 Audio Unit 的內(nèi)容,還可以參考上面講到的 Audio Unit Framework笨觅。

Audio Unit v3 Plug-Ins[60]:基于 AUv3 應(yīng)用擴(kuò)展接口來提供自定義的音效拦耐、樂器及其他音頻能力。

Audio Components[61]:定義了發(fā)現(xiàn)见剩、加載杀糯、配置和關(guān)閉音頻組件(包括音頻單元(audio unit)、音頻編解碼器(audio codec))的接口苍苞。

Audio Unit v2 (C) API[62]:配置一個(gè)音頻單元(audio unit)以及進(jìn)行音頻渲染固翰。

Audio Unit Properties[63]:獲取有關(guān)內(nèi)置混音器、均衡器羹呵、濾波器骂际、特效及音頻應(yīng)用擴(kuò)展的信息。

Audio Unit Voice I/O[64]:配置系統(tǒng)語(yǔ)音處理冈欢、響應(yīng)語(yǔ)音事件歉铝。

2)Playback and Recording[65]:音頻播放和錄制。

Audio Queue Services[66]:提供了簡(jiǎn)單的凑耻、低開銷的方式來錄制和播放音頻的 C 語(yǔ)言接口太示。支持 Linear PCM柠贤、AAC 的錄制和播放。實(shí)現(xiàn)了連接音頻硬件类缤、管理內(nèi)存臼勉、根據(jù)需要使用解碼器解碼音頻、調(diào)解錄音和播放餐弱。但是要實(shí)現(xiàn)低延遲坚俗、回聲消除、混音等功能岸裙,還得使用 AudioUnit猖败。

Audio Services[67]:提供了一組 C 語(yǔ)言接口來實(shí)現(xiàn)播放短聲或觸發(fā) iOS 設(shè)備的振動(dòng)效果。

Music Player[68]:支持播放一組音軌降允,并管理播放的各種的事件恩闻。

3)Audio Files and Formats[69]:音頻文件和格式。

Audio Format Services[70]:獲取音頻格式和編解碼器的信息剧董。

Audio File Services[71]:從磁盤或內(nèi)存讀寫各種音頻數(shù)據(jù)幢尚。

Extended Audio File Services[72]:通過組合 Audio File Services 和 Audio Converter Services 來提供讀寫音頻編碼文件或 LPCM 音頻文件。

Audio File Stream Services[73]:解析音頻流數(shù)據(jù)翅楼。

Audio File Components[74]:獲取音頻文件格式以及文件中包含的數(shù)據(jù)的信息尉剩。

Core Audio File Format[75]:解析 Core Audio 文件的結(jié)構(gòu)。

4)Utilities[76]:其他音頻功能支持毅臊。

Audio Converter Services[77]:音頻編解碼理茎。支持 LPCM 各種格式轉(zhuǎn)換靶庙,以及 LPCM 與編碼格式(如 AAC)的轉(zhuǎn)換闻妓。常用的接口:

AudioConverterNew(...)[78]:根據(jù)指定的輸入和輸出音頻格式創(chuàng)建對(duì)應(yīng)的轉(zhuǎn)換器(編解碼器)實(shí)例示绊。

AudioConverterNewSpecific(...)[79]:根據(jù)指定的 codec 來創(chuàng)建一個(gè)新的音頻轉(zhuǎn)換器(編解碼器)實(shí)例莉兰。

AudioConverterReset(...)[80]:重置音頻轉(zhuǎn)換器(編解碼器)實(shí)例,并清理它的緩沖區(qū)逗爹。

AudioConverterDispose(...)[81]:釋放音頻轉(zhuǎn)換器(編解碼器)實(shí)例飒硅。

AudioConverterGetProperty(...)[82]:獲取音頻轉(zhuǎn)換器(編解碼器)的屬性徒蟆。

AudioConverterSetProperty(...)[83]:設(shè)置音頻轉(zhuǎn)換器(編解碼器)的屬性胎挎。

AudioConverterConvertBuffer(...)[84]:只用于一種特殊的情況下將音頻數(shù)據(jù)從一種 LPCM 格式轉(zhuǎn)換為另外一種沟启,并且前后采樣率一致。這個(gè)接口不支持大多數(shù)壓縮編碼格式犹菇。

AudioConverterFillComplexBuffer(...)[85]:轉(zhuǎn)換(編碼)回調(diào)函數(shù)提供的音頻數(shù)據(jù)德迹,支持不交錯(cuò)和包格式。大部分情況下都建議用這個(gè)接口项栏,除非是要將音頻數(shù)據(jù)從一種 LPCM 格式轉(zhuǎn)換為另外一種浦辨。

AudioConverterComplexInputDataProc[86]:為AudioConverterFillComplexBuffer(...)接口提供輸入數(shù)據(jù)的回調(diào)蹬竖。

Audio Codec[87]:提供了支持將音頻數(shù)據(jù)進(jìn)行編碼格式轉(zhuǎn)換的 API沼沈。具體支持哪些編碼格式取決于系統(tǒng)提供了哪些編解碼器流酬。

5、AVFoundation Framework

AVFoundation Framework[88]是更上層的面向?qū)ο蟮囊粋€(gè)音視頻處理框架列另。它提供了音視頻資源管理芽腾、相機(jī)設(shè)備管理、音視頻處理页衙、系統(tǒng)級(jí)音頻交互管理的能力摊滔,功能非常強(qiáng)大。如果對(duì)其功能進(jìn)行細(xì)分店乐,可以分為如下幾個(gè)模塊:

Assets艰躺,音視頻資源管理。

Playback眨八,媒體播放及自定義播放行為支持腺兴。

Capture,內(nèi)置及外置的相機(jī)廉侧、麥克風(fēng)等采集設(shè)備管理页响,圖片、音視頻錄制段誊。

Editing闰蚕,音視頻編輯。

Audio连舍,音頻播放没陡、錄制和處理,App 系統(tǒng)音頻行為配置索赏。

Speech诗鸭,文本語(yǔ)音轉(zhuǎn)換。

在我們前面的 Demo 中封裝 Muxer 和 Demuxer 及設(shè)置 AudioSession 時(shí)會(huì)用到 AVFoundation Framework 的一些能力参滴,我們這里對(duì)應(yīng)地介紹一下强岸。

AVAssetWriter[89]:支持將媒體數(shù)據(jù)寫入 QuickTime 或 MPEG-4 格式的文件中,支持對(duì)多軌道的媒體數(shù)據(jù)進(jìn)行交錯(cuò)處理來提高播放和存儲(chǔ)的效率砾赔,支持對(duì)媒體采樣進(jìn)行轉(zhuǎn)碼蝌箍,支持寫入 metadata。需要注意的是暴心,一個(gè) AVAssetWriter 實(shí)例只能對(duì)應(yīng)寫一個(gè)文件妓盲,如果要寫入多個(gè)文件,需要?jiǎng)?chuàng)建多個(gè) AVAssetWriter 實(shí)例专普。

canAddInput:[90]:檢查 AVAssetWriter 是否支持添加對(duì)應(yīng)的 AVAssetWriterInput悯衬。

addInput:[91]:給 AVAssetWriter 添加一個(gè) AVAssetWriterInput。注意必須在 AVAssetWriter 開始寫入之前添加檀夹。

startWriting[92]:開始寫入筋粗。必須在配置好 AVAssetWriter 添加完 AVAssetWriterInput 做好準(zhǔn)備后再調(diào)用這個(gè)方法策橘。在調(diào)用完這個(gè)方法后,需要調(diào)用startSessionAtSourceTime:開始寫入會(huì)話娜亿,此后就可以使用對(duì)應(yīng)的 AVAssetWriterInput 來寫入媒體采樣數(shù)據(jù)丽已。

startSessionAtSourceTime:[93]:開啟寫入會(huì)話。在startWriting后調(diào)用买决,在寫入媒體采樣數(shù)據(jù)之前調(diào)用沛婴。

endSessionAtSourceTime:[94]:結(jié)束寫入會(huì)話。結(jié)束時(shí)間是會(huì)話結(jié)束時(shí)樣本數(shù)據(jù)在時(shí)間軸上的時(shí)刻督赤。如果沒有顯示調(diào)用這個(gè)方法嘁灯,系統(tǒng)會(huì)在你調(diào)用finishWritingWithCompletionHandler: 結(jié)束寫入時(shí)自動(dòng)調(diào)用。

finishWritingWithCompletionHandler:[95]:標(biāo)記 AVAssetWriter 的所有 input 為結(jié)束躲舌,完成寫入旁仿。為了保證 AVAssetWriter 完成所有采樣數(shù)據(jù)的寫入,要在調(diào)用添加數(shù)據(jù)正確返回后調(diào)用這個(gè)方法孽糖。

cancelWriting[96]:取消創(chuàng)建輸出文件枯冈。如果 AVAssetWriter 的狀態(tài)是 Failed 或 Completed,調(diào)用這個(gè)方法無效办悟,否則尘奏,調(diào)用它會(huì)阻塞調(diào)用線程,直到會(huì)話取消完成病蛉。如果 AVAssetWriter 已經(jīng)創(chuàng)建了輸出文件炫加,調(diào)用這個(gè)方法會(huì)刪除這個(gè)文件。

AVAssetWriterInput[97]:用于向 AVAssetWriter 實(shí)例的輸出文件的一個(gè)軌道添加媒體采樣數(shù)據(jù)铺然。一個(gè)實(shí)例只能對(duì)應(yīng)一個(gè)軌道媒體數(shù)據(jù)或 metadata 數(shù)據(jù)的寫入俗孝,當(dāng)使用多個(gè)實(shí)例向多個(gè)軌道寫入數(shù)據(jù)時(shí),需要注意檢查 AVAssetWriterInput 的 readyForMoreMediaData 屬性魄健。

expectsMediaDataInRealTime[98]:輸入是否為實(shí)時(shí)數(shù)據(jù)源赋铝,比如相機(jī)采集。當(dāng)設(shè)置這個(gè)值為 YES 時(shí)沽瘦,會(huì)優(yōu)化用于實(shí)時(shí)使用的輸入來精準(zhǔn)計(jì)算readyForMoreMediaData的狀態(tài)革骨。

readyForMoreMediaData[99]:表示 AVAssetWriterInput 是否已經(jīng)準(zhǔn)備好接收媒體數(shù)據(jù)。

requestMediaDataWhenReadyOnQueue:usingBlock:[100]:告訴 AVAssetWriterInput 在方便的時(shí)候去請(qǐng)求數(shù)據(jù)并寫入輸出文件析恋。在對(duì)接拉取式的數(shù)據(jù)源時(shí)良哲,可以用這個(gè)方法。

appendSampleBuffer:[101]:通過 AVAssetWriterInput 向輸出文件添加媒體數(shù)據(jù)助隧,但是添加之前媒體數(shù)據(jù)的順序需要自己處理筑凫。注意,調(diào)用這個(gè)方法添加采樣數(shù)據(jù)后,不要更改采樣數(shù)據(jù)的內(nèi)容巍实。

markAsFinished[102]:標(biāo)記 AVAssetWriterInput 為完成滓技,表示已經(jīng)完成向它添加媒體數(shù)據(jù)了。

AVAssetReader[103]:用于從 AVAsset 資源中讀取媒體數(shù)據(jù)蔫浆。這個(gè) AVAsset 可以是 QuickTime 或 MPEG-4 文件,也可以是編輯創(chuàng)作的 AVComposition姐叁。

canAddOutput:[104]:檢查 AVAssetReader 是否支持添加對(duì)應(yīng)的 AVAssetReaderOutput瓦盛。

addOutput:[105]:給 AVAssetReader 添加一個(gè) AVAssetReaderOutput。注意必須在 AVAssetReader 開始讀取之前添加外潜。

startReading[106]:開始讀取原环。

cancelReading[107]:在讀完數(shù)據(jù)之前取消讀取可以調(diào)用這個(gè)接口。

AVAssetReaderOutput[108]:一個(gè)抽象類处窥,定義了從 AVAsset 資源中讀取媒體采樣數(shù)據(jù)的接口嘱吗。通常我們可以使用AVAssetReaderTrackOutput、AVAssetReaderVideoCompositionOutput等具體的實(shí)現(xiàn)類滔驾。

AVAssetReaderTrackOutput[109]

alwaysCopiesSampleData[110]:是否總是拷貝采樣數(shù)據(jù)谒麦。如果要修改讀取的采樣數(shù)據(jù),可以設(shè)置 YES哆致,否則就設(shè)置 NO绕德,這樣性能會(huì)更好。

copyNextSampleBuffer[111]:從 Output 拷貝下一個(gè) CMSampleBuffer摊阀。

AVAudioSession[112]:在最新版本的 iOS 系統(tǒng)庫(kù)中耻蛇,AVAudioSession 已經(jīng)遷移到 AVFAudio Framework 中了。AVAudioSession 是系統(tǒng)用來管理 App 對(duì)音頻硬件資源的使用的胞此,比如:設(shè)置當(dāng)前 App 與其他 App 同時(shí)使用音頻時(shí)臣咖,是否混音、打斷或降低其他 App 的聲音漱牵;手機(jī)靜音鍵打開時(shí)是否還可以播放聲音夺蛇;指定音頻輸入或者輸出設(shè)備;是否支持錄制或邊錄制邊播放酣胀;聲音被打斷時(shí)的通知蚊惯。我們這里只簡(jiǎn)單介紹下 Demo 中用到的接口:

setCategory:withOptions:error:[113]:設(shè)置 AudioSession 的類型和選項(xiàng)參數(shù)。比如類型為 AVAudioSessionCategoryPlayback 表示支持播放灵临;AVAudioSessionCategoryPlayAndRecord 表示同時(shí)支持播放和錄制等等截型。

setMode:error:[114]:設(shè)置 AudioSession 的模式。AudioSession 的類型和模式一起決定了 App 如何使用音頻儒溉。通常需要在激活 AudioSession 之前設(shè)置類型和模式宦焦。比如模式為 AVAudioSessionModeVideoRecording 表示當(dāng)期要錄制視頻;AVAudioSessionModeVoiceChat 表示語(yǔ)音聊天。

setActive:withOptions:error:[115]:激活或釋放 AudioSession 的使用波闹。

以上這些框架及 API 基本上可以覆蓋我們?cè)谇懊娴?Demo 中用到的能力了酝豪。

參考資料

[1]Audio Unit Hosting Guide for iOS:https://developer.apple.com/library/archive/documentation/MusicAudio/Conceptual/AudioUnitHostingGuide_iOS/AudioUnitHostingFundamentals/AudioUnitHostingFundamentals.html

[2]Core Audio Overview:https://developer.apple.com/library/archive/documentation/MusicAudio/Conceptual/CoreAudioOverview/CoreAudioEssentials/CoreAudioEssentials.html

[3]Audio Unit:https://developer.apple.com/documentation/audiounit?language=objc

[4]Audio Component Services:https://developer.apple.com/documentation/audiounit/audio_component_services?language=objc

[5]AudioComponent:https://developer.apple.com/documentation/audiotoolbox/audiocomponent?language=objc

[6]AudioComponentDescription:https://developer.apple.com/documentation/audiotoolbox/audiocomponentdescription?language=objc

[7]AudioComponentInstance:https://developer.apple.com/documentation/audiotoolbox/audiocomponentinstance?language=objc

[8]AudioComponentFindNext(...):https://developer.apple.com/documentation/audiotoolbox/1410445-audiocomponentfindnext?language=objc

[9]AudioComponentGetDescription(...):https://developer.apple.com/documentation/audiotoolbox/1410523-audiocomponentgetdescription?language=objc

[10]AudioComponentInstanceNew(...):https://developer.apple.com/documentation/audiotoolbox/1410465-audiocomponentinstancenew?language=objc

[11]AudioComponentInstanceDispose(...):https://developer.apple.com/documentation/audiotoolbox/1410508-audiocomponentinstancedispose

[12]Audio Unit Component Services:https://developer.apple.com/documentation/audiounit/audio_component_services?language=objc

[13]AudioUnit:https://developer.apple.com/documentation/audiotoolbox/audiounit?language=objc

[14]AudioUnitParameter:https://developer.apple.com/documentation/audiotoolbox/audiounitparameter?language=objc

[15]AudioUnitProperty:https://developer.apple.com/documentation/audiotoolbox/audiounitproperty?language=objc

[16]AudioUnitInitialize(...):https://developer.apple.com/documentation/audiotoolbox/1439851-audiounitinitialize?language=objc

[17]AudioUnitUninitialize(...):https://developer.apple.com/documentation/audiotoolbox/1438415-audiounituninitialize?language=objc

[18]AudioUnitRender(...):https://developer.apple.com/documentation/audiotoolbox/1438430-audiounitrender?language=objc

[19]AudioUnitGetProperty(...):https://developer.apple.com/documentation/audiotoolbox/1439840-audiounitgetproperty?language=objc

[20]AudioUnitSetProperty(...):https://developer.apple.com/documentation/audiotoolbox/1440371-audiounitsetproperty?language=objc

[21]AudioUnitGetParameter(...):https://developer.apple.com/documentation/audiotoolbox/1440055-audiounitgetparameter?language=objc

[22]AudioUnitSetParameter(...):https://developer.apple.com/documentation/audiotoolbox/1438454-audiounitsetparameter?language=objc

[23]AURenderCallback:https://developer.apple.com/documentation/audiotoolbox/aurendercallback?language=objc

[24]AudioUnitPropertyListenerProc:https://developer.apple.com/documentation/audiotoolbox/audiounitpropertylistenerproc?language=objc

[25]Output Audio Unit Services:https://developer.apple.com/documentation/audiounit/output_audio_unit_services?language=objc

[26]AudioOutputUnitStart(...):https://developer.apple.com/documentation/audiotoolbox/1439763-audiooutputunitstart?language=objc

[27]AudioOutputUnitStop(...):https://developer.apple.com/documentation/audiotoolbox/1440513-audiooutputunitstop?language=objc

[28]Core Media:https://developer.apple.com/documentation/coremedia?language=objc

[29]Sample Processing:https://developer.apple.com/documentation/coremedia?language=objc

[30]CMSampleBuffer:https://developer.apple.com/documentation/coremedia/cmsamplebuffer-u71?language=objc

[31]CMSampleBufferCreateReady(...):https://developer.apple.com/documentation/coremedia/1489513-cmsamplebuffercreateready?language=objc

[32]CMSampleBufferCreate(...):https://developer.apple.com/documentation/coremedia/1489723-cmsamplebuffercreate?language=objc

[33]CMSampleBufferSetDataBufferFromAudioBufferList(...):https://developer.apple.com/documentation/coremedia/1489725-cmsamplebuffersetdatabufferfroma?language=objc

[34]CMSampleBufferGetFormatDescription(...):https://developer.apple.com/documentation/coremedia/1489185-cmsamplebuffergetformatdescripti?language=objc

[35]CMSampleBufferGetDataBuffer(...):https://developer.apple.com/documentation/coremedia/1489629-cmsamplebuffergetdatabuffer?language=objc

[36]CMSampleBufferGetPresentationTimeStamp(...):https://developer.apple.com/documentation/coremedia/1489252-cmsamplebuffergetpresentationtim?language=objc

[37]CMBlockBuffer:https://developer.apple.com/documentation/coremedia/cmblockbuffer-u9i?language=objc

[38]CMBlockBufferCreateWithMemoryBlock(...):https://developer.apple.com/documentation/coremedia/1489501-cmblockbuffercreatewithmemoryblo?language=objc

[39]CMBlockBufferGetDataPointer(...):https://developer.apple.com/documentation/coremedia/1489264-cmblockbuffergetdatapointer?language=objc

[40]CMFormatDescription:https://developer.apple.com/documentation/coremedia/cmformatdescription-u8g?language=objc

[41]CMFormatDescriptionCreate(...):https://developer.apple.com/documentation/coremedia/1489182-cmformatdescriptioncreate?language=objc

[42]CMAudioFormatDescription:https://developer.apple.com/documentation/coremedia/cmaudioformatdescription?language=objc

[43]CMAudioFormatDescriptionCreate(...):https://developer.apple.com/documentation/coremedia/1489522-cmaudioformatdescriptioncreate?language=objc

[44]CMAudioFormatDescriptionGetStreamBasicDescription(...):https://developer.apple.com/documentation/coremedia/1489226-cmaudioformatdescriptiongetstrea?language=objc

[45]CMAttachment:https://developer.apple.com/documentation/coremedia/cmattachment?language=objc

[46]AudioStreamBasicDescription:https://developer.apple.com/documentation/coreaudiotypes/audiostreambasicdescription?language=objc

[47]AudioBuffer:https://developer.apple.com/documentation/coreaudiotypes/audiobuffer?language=objc

[48]AudioBufferList:https://developer.apple.com/documentation/coreaudiotypes/audiobufferlist?language=objc

[49]AudioTimeStamp:https://developer.apple.com/documentation/coreaudiotypes/audiotimestamp?language=objc

[50]Time Representation:https://developer.apple.com/documentation/coremedia?language=objc

[51]CMTime:https://developer.apple.com/documentation/coremedia/cmtime-u58?language=objc

[52]CMTimeRange:https://developer.apple.com/documentation/coremedia/cmtimerange-qts?language=objc

[53]CMSampleTimingInfo:https://developer.apple.com/documentation/coremedia/cmsampletiminginfo?language=objc

[54]Queues:https://developer.apple.com/documentation/coremedia?language=objc

[55]CMSimpleQueue:https://developer.apple.com/documentation/coremedia/cmsimplequeue?language=objc

[56]CMBufferQueue:https://developer.apple.com/documentation/coremedia/cmbufferqueue?language=objc

[57]CMMemoryPool:https://developer.apple.com/documentation/coremedia/cmmemorypool-u89?language=objc

[58]Audio Toolbox:https://developer.apple.com/documentation/audiotoolbox?language=objc

[59]Audio Units:https://developer.apple.com/documentation/audiotoolbox?language=objc

[60]Audio Unit v3 Plug-Ins:https://developer.apple.com/documentation/audiotoolbox/audio_unit_v3_plug-ins?language=objc

[61]Audio Components:https://developer.apple.com/documentation/audiotoolbox/audio_components?language=objc

[62]Audio Unit v2 (C) API:https://developer.apple.com/documentation/audiotoolbox/audio_unit_v2_c_api?language=objc

[63]Audio Unit Properties:https://developer.apple.com/documentation/audiotoolbox/audio_unit_properties?language=objc

[64]Audio Unit Voice I/O:https://developer.apple.com/documentation/audiotoolbox/audio_unit_voice_i_o?language=objc

[65]Playback and Recording:https://developer.apple.com/documentation/audiotoolbox?language=objc

[66]Audio Queue Services:https://developer.apple.com/documentation/audiotoolbox/audio_queue_services?language=objc

[67]Audio Services:https://developer.apple.com/documentation/audiotoolbox/audio_services?language=objc

[68]Music Player:https://developer.apple.com/documentation/audiotoolbox/music_player?language=objc

[69]Audio Files and Formats:https://developer.apple.com/documentation/audiotoolbox?language=objc

[70]Audio Format Services:https://developer.apple.com/documentation/audiotoolbox/audio_format_services?language=objc

[71]Audio File Services:https://developer.apple.com/documentation/audiotoolbox/audio_file_services?language=objc

[72]Extended Audio File Services:https://developer.apple.com/documentation/audiotoolbox/extended_audio_file_services?language=objc

[73]Audio File Stream Services:https://developer.apple.com/documentation/audiotoolbox/audio_file_stream_services?language=objc

[74]Audio File Components:https://developer.apple.com/documentation/audiotoolbox/audio_file_components?language=objc

[75]Core Audio File Format:https://developer.apple.com/documentation/audiotoolbox/core_audio_file_format?language=objc

[76]Utilities:https://developer.apple.com/documentation/audiotoolbox?language=objc

[77]Audio Converter Services:https://developer.apple.com/documentation/audiotoolbox/audio_converter_services?language=objc

[78]AudioConverterNew(...):https://developer.apple.com/documentation/audiotoolbox/1502936-audioconverternew?language=objc

[79]AudioConverterNewSpecific(...):https://developer.apple.com/documentation/audiotoolbox/1503356-audioconverternewspecific?language=objc

[80]AudioConverterReset(...):https://developer.apple.com/documentation/audiotoolbox/1503102-audioconverterreset?language=objc

[81]AudioConverterDispose(...):https://developer.apple.com/documentation/audiotoolbox/1502671-audioconverterdispose?language=objc

[82]AudioConverterGetProperty(...):https://developer.apple.com/documentation/audiotoolbox/1502731-audioconvertergetproperty?language=objc

[83]AudioConverterSetProperty(...):https://developer.apple.com/documentation/audiotoolbox/1501675-audioconvertersetproperty?language=objc

[84]AudioConverterConvertBuffer(...):https://developer.apple.com/documentation/audiotoolbox/1503345-audioconverterconvertbuffer?language=objc

[85]AudioConverterFillComplexBuffer(...):https://developer.apple.com/documentation/audiotoolbox/1503098-audioconverterfillcomplexbuffer?language=objc

[86]AudioConverterComplexInputDataProc:https://developer.apple.com/documentation/audiotoolbox/audioconvertercomplexinputdataproc?language=objc

[87]Audio Codec:https://developer.apple.com/documentation/audiotoolbox/audio_codec?language=objc

[88]AVFoundation Framework:https://developer.apple.com/documentation/avfoundation?language=objc

[89]AVAssetWriter:https://developer.apple.com/documentation/avfoundation/avassetwriter?language=objc

[90]canAddInput::https://developer.apple.com/documentation/avfoundation/avassetwriter/1387863-canaddinput?language=objc

[91]addInput::https://developer.apple.com/documentation/avfoundation/avassetwriter/1390389-addinput?language=objc

[92]startWriting:https://developer.apple.com/documentation/avfoundation/avassetwriter/1386724-startwriting?language=objc

[93]startSession(atSourceTime:):https://developer.apple.com/documentation/avfoundation/avassetwriter/1389908-startsessionatsourcetime?language=objc

[94]endSessionAtSourceTime::https://developer.apple.com/documentation/avfoundation/avassetwriter/1389921-endsessionatsourcetime?language=objc

[95]finishWritingWithCompletionHandler::https://developer.apple.com/documentation/avfoundation/avassetwriter/1390432-finishwriting?language=objc

[96]cancelWriting:https://developer.apple.com/documentation/avfoundation/avassetwriter/1387234-cancelwriting?language=objc

[97]AVAssetWriterInput:https://developer.apple.com/documentation/avfoundation/avassetwriterinput?language=objc

[98]expectsMediaDataInRealTime:https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1387827-expectsmediadatainrealtime?language=objc

[99]readyForMoreMediaData:https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1389084-readyformoremediadata?language=objc

[100]requestMediaDataWhenReadyOnQueue:usingBlock::https://developer.apple.com/documentation/avfoundation/avassetwriterinput?language=objc

[101]appendSampleBuffer::https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1389566-appendsamplebuffer?language=objc

[102]markAsFinished:https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1390122-markasfinished?language=objc

[103]AVAssetReader:https://developer.apple.com/documentation/avfoundation/avassetreader?language=objc

[104]canAddOutput::https://developer.apple.com/documentation/avfoundation/avassetreader/1387485-canaddoutput?language=objc

[105]addOutput::https://developer.apple.com/documentation/avfoundation/avassetreader/1390110-addoutput?language=objc

[106]startReading:https://developer.apple.com/documentation/avfoundation/avassetreader/1390286-startreading?language=objc

[107]cancelReading:https://developer.apple.com/documentation/avfoundation/avassetreader/1390258-cancelreading?language=objc

[108]AVAssetReaderOutput:https://developer.apple.com/documentation/avfoundation/avassetreaderoutput?language=objc

[109]AVAssetReaderTrackOutput:https://developer.apple.com/documentation/avfoundation/avassetreadertrackoutput?language=objc

[110]alwaysCopiesSampleData:https://developer.apple.com/documentation/avfoundation/avassetreaderoutput/1389189-alwayscopiessampledata?language=objc

[111]copyNextSampleBuffer:https://developer.apple.com/documentation/avfoundation/avassetreaderoutput/1385732-copynextsamplebuffer?language=objc

[112]AVAudioSession:https://developer.apple.com/documentation/avfaudio/avaudiosession?language=objc

[113]setCategory:withOptions:error::https://developer.apple.com/documentation/avfaudio/avaudiosession/1616442-setcategory?language=objc

[114]setMode:error::https://developer.apple.com/documentation/avfaudio/avaudiosession/1616614-setmode?language=objc

[115]setActive:withOptions:error::https://developer.apple.com/documentation/avfaudio/avaudiosession?language=objc

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市精堕,隨后出現(xiàn)的幾起案子孵淘,更是在濱河造成了極大的恐慌,老刑警劉巖歹篓,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瘫证,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡庄撮,警方通過查閱死者的電腦和手機(jī)背捌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來洞斯,“玉大人毡庆,你說我怎么就攤上這事±尤纾” “怎么了么抗?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)亚铁。 經(jīng)常有香客問我乖坠,道長(zhǎng),這世上最難降的妖魔是什么刀闷? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任熊泵,我火速辦了婚禮,結(jié)果婚禮上甸昏,老公的妹妹穿的比我還像新娘顽分。我一直安慰自己,他們只是感情好施蜜,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布卒蘸。 她就那樣靜靜地躺著,像睡著了一般翻默。 火紅的嫁衣襯著肌膚如雪缸沃。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天修械,我揣著相機(jī)與錄音趾牧,去河邊找鬼。 笑死肯污,一個(gè)胖子當(dāng)著我的面吹牛翘单,可吹牛的內(nèi)容都是我干的吨枉。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼哄芜,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼貌亭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起认臊,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤圃庭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后失晴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體剧腻,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年师坎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了恕酸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片堪滨。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡胯陋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出袱箱,到底是詐尸還是另有隱情遏乔,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布发笔,位于F島的核電站盟萨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏了讨。R本人自食惡果不足惜捻激,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望前计。 院中可真熱鬧胞谭,春花似錦、人聲如沸男杈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)伶棒。三九已至旺垒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間肤无,已是汗流浹背先蒋。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留宛渐,地道東北人鞭达。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓司忱,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親畴蹭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子坦仍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

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