一墓臭、整體播放策略
(1)iOS自帶的播放控件
- AVPlayer蘸鲸、AVQueuePlayer
只能通過(guò)路徑播放妖谴,可以播放本地視頻與云視頻
- AVSampleBufferDisplayLayer
可以實(shí)現(xiàn)按幀播放
(2)第三方
- ijkPlayer
默認(rèn)只支持按路徑播放窿锉,可以通過(guò)IO管道的方式,改為按幀播放方式
(3)組合方式
系統(tǒng)硬解VideoToolBox/ffmpeg軟解 + OpenGL顯示
二膝舅、VideoToolBox硬解
CMSampleBuffer = CMTime + FormatDesc(sps嗡载,pps) + CMBlockBuffer
CMBlockBuffer:編碼的圖像數(shù)據(jù)結(jié)構(gòu)
CVPixelBuffer:解碼后的圖像數(shù)據(jù)結(jié)構(gòu)
VTDecompressionSessionRef decodeSession
(CMSampleBuffer+decodeSession)—>VTDecompressionSessionDecodeFrame—>CVPixelBuffer
三、ffmpeg解碼
AVPacket—>AVFrame
1仍稀、創(chuàng)建AVCodec codec = avcodec_find_decoder(AV_CODEC_ID_H264)
2洼滚、創(chuàng)建AVCodecContext codecCtx = avcodec_alloc_context3(codec);
3、avcodec_open2(codecCtx, codec, NULL)
4技潘、視頻幀組裝成 AVPacket
5遥巴、avcodec_send_packet(codecCtx, &packet)
6、avcodec_receive_frame(codecCtx, AVFrame)
(1)AVFrame中取到Y(jié)UV數(shù)據(jù)(這里的是非交叉的)
Y:
AVFrame->data[0] 存Y數(shù)據(jù)的buffer
AVFrame->linesize[0] Y數(shù)據(jù)一段的長(zhǎng)度享幽,一般等于像素width铲掐。有幾段呢,就得看height
AVFrame->data[1] 存U數(shù)據(jù)的buffer
AVFrame->linesize[1] U數(shù)據(jù)一段的長(zhǎng)度值桩,一般等于像素width/2摆霉。有幾段呢,就得看height/2
AVFrame->data[2] 存V數(shù)據(jù)的buffer
AVFrame->linesize[2] V數(shù)據(jù)一段的長(zhǎng)度奔坟,一般等于像素width/2携栋。有幾段呢,就得看height/2
比如存到NDData里面:
width = MIN(linesize, width);
NSMutableData *md = [NSMutableData dataWithLength: width * height];
Byte *dst = (Byte *)md.mutableBytes;
for (NSUInteger i = 0; i < height; ++i) {
memcpy(dst, src, width);
dst += width;
src += linesize;
}
return md;
AVFrame->data只能以linesize作為取值累積量咳秉,這樣才不會(huì)出現(xiàn)越界或者取錯(cuò)數(shù)據(jù)
(2)IOS硬解得到的CVImageBufferRef婉支,其中的YUV是交叉存儲(chǔ)的
CVImageBufferRef imageBuffer =CMSampleBufferGetImageBuffer(sampleBuffer);
四、ffmpeg其他一些重要信息
(1)主要結(jié)構(gòu)體
AVFormatContext:文件信息上下文澜建,其中就包含了流信息結(jié)構(gòu)AVStream
AVStream:流信息磅摹,其中就包含具體的音視頻格式信息上下文AVCodecContext
AVCodecContext:具體的音視頻格式信息滋迈。要解碼音視頻,需要從這里拿到相關(guān)信息户誓,比如AVCodecID
AVPacket:未解碼的音視頻內(nèi)容饼灿。比如視頻的每一幀數(shù)據(jù),就體現(xiàn)在一個(gè)packet中
AVFrame:解碼后的音視頻內(nèi)容
(2)H264的兩種格式 Annex-B與AVCC
- Annex-B
0000000167(SPS)+0000000168(PPS)+0000000165(IDR)+其他幀
- AVCC
extradata+NAL長(zhǎng)度+NAL+NAL長(zhǎng)度+NAL帝美。碍彭。。
NAL長(zhǎng)度所占字節(jié)悼潭、SPS與PPS等包含在extradata中
(3)extradata
解碼AVCC需要與視頻合成都需要extradata庇忌,如何構(gòu)造extradata
- extradata(Annex-B)
- extradata(AVCC)
- extradata(Annex-B) 轉(zhuǎn)換為 extradata(AVCC)
(4)timebase
ffmpeg中的一種時(shí)間單位,包含了與秒的對(duì)應(yīng)關(guān)系舰褪,都可以轉(zhuǎn)換為秒皆疹。
- pts,dts占拍,duration
pts * timebase = 以秒為單位的值
(5)解碼流程
- 解Annex-B的H264
- 解AVCC的H264
(6)編碼流程
- 生成AVCodecContext略就,要設(shè)置分辨率,pix_fmt等參數(shù)
- avcodec_open2(codecCtx, codec, NULL)
- avcodec_send_frame(codecCtx, frame);
- avcodec_receive_packet(codecCtx, pkt);
(7)H264 合成mp4流程
- avformat_alloc_output_context2 創(chuàng)建輸出的文件
- avformat_new_stream 創(chuàng)建視頻流
- 構(gòu)造AVCodecContext晃酒,賦值給AVStream->codecpar
- 設(shè)置timebase表牢,AVStream->time_base
- avio_open打開(kāi)輸出文件
- avformat_write_header寫入頭
- av_interleaved_write_frame 寫入packet
- av_write_trailer 寫入結(jié)尾
五、OpenGL
簡(jiǎn)單說(shuō)就是直接操作GPU進(jìn)行圖片的渲染贝次。OpenGL有自己的渲染管線崔兴,提供給外部修改的有頂點(diǎn)處理和片元處理。
頂點(diǎn)處理:我們可以為OpenGL提供頂點(diǎn)蛔翅,相當(dāng)于構(gòu)建了圖片的外部框架(頂點(diǎn)坐標(biāo))
片元處理:提供圖片紋理填充剛才構(gòu)建的外部框架的內(nèi)容 (通過(guò)紋理坐標(biāo)可以控制圖片的方向)
然后OpenGL就幫我們寫入到幀緩沖區(qū):glDraw
幀緩沖區(qū)不能直接顯示敲茄,還得關(guān)聯(lián)一個(gè)渲染緩沖區(qū),渲染緩沖區(qū)又與某個(gè)CAEAGLLayer對(duì)應(yīng)山析。這樣就可以顯示出來(lái):presentRenderbuffer
傳值:CPU的值堰燎,先傳到GPU,GPU再傳給OpenGL繪制
OpenGL是通過(guò)shader語(yǔ)言操作的盖腿,通過(guò)shader語(yǔ)音來(lái)操控頂點(diǎn)與片元
六爽待、ijkPlayer的整體流程
三個(gè)線程 讀線程+解碼線程+顯示線程
讀線程:不斷從視頻文件通過(guò)ffmpeg讀取到AVPacket嫂沉,存到videoq隊(duì)列里面
解碼線程:從videoq拿出AVPacket谍咆,通過(guò)ffmpeg解碼得到AVFrame,然后轉(zhuǎn)化為Frame結(jié)構(gòu)體灌具,存到picq隊(duì)列
顯示線程:不斷從picq取出Frame茂卦,然后通過(guò)OpenGL渲染
七何什、音頻相關(guān)
每個(gè)采樣都得用一個(gè)數(shù)據(jù)類型來(lái)存儲(chǔ),可以是float等龙、uint16等处渣。這個(gè)會(huì)決定音頻的大小
1伶贰、音頻解碼
- ffmpeg軟解
需要參數(shù):sample_rate(采樣率)、channels(聲道數(shù))罐栈、sample_fmt(AV_SAMPLE_FMT_S16黍衙,存儲(chǔ)采樣的數(shù)據(jù)格式)、channel_layout(類似channels荠诬,兩個(gè)常量:?jiǎn)温暤览欧⒘Ⅲw聲)
nb_samples:一包(幀)acc的一個(gè)channel有多少采樣。解碼成pcm之后柑贞,就是通過(guò)這些采樣來(lái)計(jì)算buf大小方椎,用來(lái)存解碼后的pcm數(shù)據(jù)(av_samples_get_buffer_size)注:一般1024個(gè)pcm數(shù)據(jù)作為ACC的一幀,一個(gè)(一幀frame)pcm就是一個(gè)采樣(非交錯(cuò)存儲(chǔ))
一般解碼后的pcm可能需要重采樣钧嘶,比如sample_fmt是16或32等棠众,或者采樣,聲道不一致有决,需要通過(guò)swr_convert轉(zhuǎn)換
非交錯(cuò)的存儲(chǔ)方式:NonInterleaved闸拿,一個(gè)frame只存一個(gè)channel,所以frame的大小等于一個(gè)channel的采樣大小
交錯(cuò)的存儲(chǔ)方式:Interleaved疮薇,一個(gè)frame存多個(gè)channel的數(shù)據(jù)胸墙,所以frame = sampleSize * channel
- AudioToolBox硬解
inPutDescription(aac)—>outPutDescription(pcm)
解碼器描述符(Description)我注,告訴系統(tǒng)使用哪個(gè)解碼器
AudioConverterFillComplexBuffer 進(jìn)行解碼按咒,input和output的數(shù)據(jù)都是通過(guò)AudioBufferList進(jìn)行傳遞 - 直接用aac庫(kù)
2、音頻編碼
ffmpeg軟編:軟解的逆過(guò)程
AudioToolBox硬編碼:硬解碼的逆過(guò)程
3但骨、音頻播放
- AudioSession
- AudioQueue
- AudioUnit