一、整體播放策略
(1)iOS自帶的播放控件
- AVPlayer、AVQueuePlayer
只能通過路徑播放,可以播放本地視頻與云視頻
- AVSampleBufferDisplayLayer
可以實現(xiàn)按幀播放
(2)第三方
- ijkPlayer
默認只支持按路徑播放,可以通過IO管道的方式,改為按幀播放方式
(3)組合方式
系統(tǒng)硬解VideoToolBox/ffmpeg軟解 + OpenGL顯示
二、VideoToolBox硬解
CMSampleBuffer = CMTime + FormatDesc(sps,pps) + CMBlockBuffer
CMBlockBuffer:編碼的圖像數(shù)據(jù)結構
CVPixelBuffer:解碼后的圖像數(shù)據(jù)結構
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中取到YUV數(shù)據(jù)(這里的是非交叉的)
Y:
AVFrame->data[0] 存Y數(shù)據(jù)的buffer
AVFrame->linesize[0] Y數(shù)據(jù)一段的長度,一般等于像素width。有幾段呢,就得看height
AVFrame->data[1] 存U數(shù)據(jù)的buffer
AVFrame->linesize[1] U數(shù)據(jù)一段的長度,一般等于像素width/2。有幾段呢,就得看height/2
AVFrame->data[2] 存V數(shù)據(jù)的buffer
AVFrame->linesize[2] V數(shù)據(jù)一段的長度,一般等于像素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作為取值累積量,這樣才不會出現(xiàn)越界或者取錯數(shù)據(jù)
(2)IOS硬解得到的CVImageBufferRef,其中的YUV是交叉存儲的
CVImageBufferRef imageBuffer =CMSampleBufferGetImageBuffer(sampleBuffer);
四、ffmpeg其他一些重要信息
(1)主要結構體
AVFormatContext:文件信息上下文,其中就包含了流信息結構AVStream
AVStream:流信息,其中就包含具體的音視頻格式信息上下文AVCodecContext
AVCodecContext:具體的音視頻格式信息。要解碼音視頻,需要從這里拿到相關信息,比如AVCodecID
AVPacket:未解碼的音視頻內(nèi)容。比如視頻的每一幀數(shù)據(jù),就體現(xiàn)在一個packet中
AVFrame:解碼后的音視頻內(nèi)容
(2)H264的兩種格式 Annex-B與AVCC
- Annex-B
0000000167(SPS)+0000000168(PPS)+0000000165(IDR)+其他幀
- AVCC
extradata+NAL長度+NAL+NAL長度+NAL。。。
NAL長度所占字節(jié)、SPS與PPS等包含在extradata中
(3)extradata
解碼AVCC需要與視頻合成都需要extradata,如何構造extradata
- extradata(Annex-B)
- extradata(AVCC)
- extradata(Annex-B) 轉換為 extradata(AVCC)
(4)timebase
ffmpeg中的一種時間單位,包含了與秒的對應關系,都可以轉換為秒。
- pts,dts,duration
pts * timebase = 以秒為單位的值
(5)解碼流程
- 解Annex-B的H264
- 解AVCC的H264
(6)編碼流程
- 生成AVCodecContext,要設置分辨率,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)建視頻流
- 構造AVCodecContext,賦值給AVStream->codecpar
- 設置timebase,AVStream->time_base
- avio_open打開輸出文件
- avformat_write_header寫入頭
- av_interleaved_write_frame 寫入packet
- av_write_trailer 寫入結尾
五、OpenGL
簡單說就是直接操作GPU進行圖片的渲染。OpenGL有自己的渲染管線,提供給外部修改的有頂點處理和片元處理。
頂點處理:我們可以為OpenGL提供頂點,相當于構建了圖片的外部框架(頂點坐標)
片元處理:提供圖片紋理填充剛才構建的外部框架的內(nèi)容 (通過紋理坐標可以控制圖片的方向)
然后OpenGL就幫我們寫入到幀緩沖區(qū):glDraw
幀緩沖區(qū)不能直接顯示,還得關聯(lián)一個渲染緩沖區(qū),渲染緩沖區(qū)又與某個CAEAGLLayer對應。這樣就可以顯示出來:presentRenderbuffer
傳值:CPU的值,先傳到GPU,GPU再傳給OpenGL繪制
OpenGL是通過shader語言操作的,通過shader語音來操控頂點與片元
六、ijkPlayer的整體流程
三個線程 讀線程+解碼線程+顯示線程
讀線程:不斷從視頻文件通過ffmpeg讀取到AVPacket,存到videoq隊列里面
解碼線程:從videoq拿出AVPacket,通過ffmpeg解碼得到AVFrame,然后轉化為Frame結構體,存到picq隊列
顯示線程:不斷從picq取出Frame,然后通過OpenGL渲染
七、音頻相關
每個采樣都得用一個數(shù)據(jù)類型來存儲,可以是float、uint16等。這個會決定音頻的大小
1、音頻解碼
- ffmpeg軟解
需要參數(shù):sample_rate(采樣率)、channels(聲道數(shù))、sample_fmt(AV_SAMPLE_FMT_S16,存儲采樣的數(shù)據(jù)格式)、channel_layout(類似channels,兩個常量:單聲道、立體聲)
nb_samples:一包(幀)acc的一個channel有多少采樣。解碼成pcm之后,就是通過這些采樣來計算buf大小,用來存解碼后的pcm數(shù)據(jù)(av_samples_get_buffer_size)注:一般1024個pcm數(shù)據(jù)作為ACC的一幀,一個(一幀frame)pcm就是一個采樣(非交錯存儲)
一般解碼后的pcm可能需要重采樣,比如sample_fmt是16或32等,或者采樣,聲道不一致,需要通過swr_convert轉換
非交錯的存儲方式:NonInterleaved,一個frame只存一個channel,所以frame的大小等于一個channel的采樣大小
交錯的存儲方式:Interleaved,一個frame存多個channel的數(shù)據(jù),所以frame = sampleSize * channel
- AudioToolBox硬解
inPutDescription(aac)—>outPutDescription(pcm)
解碼器描述符(Description),告訴系統(tǒng)使用哪個解碼器
AudioConverterFillComplexBuffer 進行解碼,input和output的數(shù)據(jù)都是通過AudioBufferList進行傳遞 - 直接用aac庫
2、音頻編碼
ffmpeg軟編:軟解的逆過程
AudioToolBox硬編碼:硬解碼的逆過程
3、音頻播放
- AudioSession
- AudioQueue
- AudioUnit