背景: 去年下半年由于種種因素驅(qū)動(dòng)下,,準(zhǔn)備去考研,,在之前同事的推薦下,參加了考研培訓(xùn)班,,培訓(xùn)班發(fā)了紙質(zhì)書籍和線上視頻觀看賬號(hào),,由于線上視頻需要全程聯(lián)網(wǎng)才能觀看,突發(fā)奇想,,要是我把這些視頻下載下來,,沒網(wǎng)的時(shí)候也能拿出來觀看復(fù)習(xí)多好,; 在此背景下,花了幾天時(shí)間簡(jiǎn)單做了一個(gè)app出來輔助復(fù)習(xí),,前段時(shí)間通過了考研復(fù)試,,這幾天利用空閑時(shí)間,完善了app功能,,同時(shí)記錄下開發(fā)過程和一些感悟,; 效果:
功能簡(jiǎn)單介紹: 在線播放(下載).m3u8視頻,PC瀏覽器查看和離線觀看! 注意: 由于涉及到培訓(xùn)班視頻的隱私問題,,這里不給出視頻播放畫面,,敬請(qǐng)諒解!
如何實(shí)現(xiàn)? 市面上眾多培訓(xùn)班的視頻一般都不是普通的.mp4視頻,很多都是經(jīng)過了加密或者非專業(yè)人士知道的特殊視頻格式,, 工作期間經(jīng)常使用抓包工具,,我想先試下抓包看看,說不定可以了解到一些信息,,從而啟發(fā)下一步,,好,說干就干....
在用charles抓包的時(shí)候,,發(fā)現(xiàn)截圖該域名下不斷的有新請(qǐng)求產(chǎn)生,,有一個(gè).M3U8請(qǐng)求地址,還有不斷產(chǎn)生的.ts.ts請(qǐng)求地址,, 網(wǎng)上搜索相關(guān)文章,,茅塞頓開,實(shí)現(xiàn)起來并不復(fù)雜,。 涉及到的技術(shù)實(shí)現(xiàn)網(wǎng)上文章很多,,當(dāng)時(shí)備考時(shí)間短,我把自己的核心需求理了下,,網(wǎng)上找相關(guān)"輪子",有滿足自己的需要就直接拿來組裝了 ,,哈哈
技術(shù)要點(diǎn) 一.iOS音視頻播放 iOS音視頻播放技術(shù)現(xiàn)在很成熟了,之前在項(xiàng)目中有涉及到視頻播放相關(guān)處理的功能,,代碼中主要使用的類是AVPlayer,個(gè)人建議首先看蘋果文檔,,最全面最權(quán)威的了,網(wǎng)上找到的不夠全面,,要綜合不同文章才能全面些,,打開位置Xcode-Window-Developer Documentation,搜索類名
官方說明該類可用來處理本地和遠(yuǎn)程基于文件的媒體播放,,例如quiktime電影,,mp3音頻文件,以及使用HLS直播提供的視聽媒體,; 使用該對(duì)象就可以直接播放前面說的.m3u8文件了,; 不過這樣會(huì)存在一個(gè)問題,,當(dāng)網(wǎng)絡(luò)不好的時(shí)候,體驗(yàn)就很糟糕了,,而且也不能離線觀看,; 看能不能處理成常用的.mp4文件,方便保存和離線觀看? 基本思路是: 批量下載.ts.ts文件=》合并文件=>轉(zhuǎn)碼成.mp4 批量下載ts.ts文件就基本的文件網(wǎng)絡(luò)下載思路了 合并文件: 用一個(gè)NSMutableData來存儲(chǔ)合并后的文件數(shù)據(jù),,循環(huán)讀取每一個(gè).ts片段數(shù)據(jù),,追加到NSMutableData 轉(zhuǎn)碼成.mp4: 這一步需要用到FFmpeg框架來做,對(duì)應(yīng)的iOS庫(kù),,一般可采用命令行的方式進(jìn)行調(diào)用,,這里的轉(zhuǎn)碼示例如下: ffmpeg -ss 00:00:00 -i /Users/Mac賬戶名/Library/Developer/CoreSimulator/Devices/FDABA415-C26F-483F-B0F8-CDC02A03054B/data/Containers/Data/Application/85D2E7FC-F389-40D3-9607-03718564AFD3/Documents/Download/9f344d0e79cbceceed64ecaa62adf709/9f344d0e79cbceceed64ecaa62adf709.ts -b:v 2000K -y /Users/Mac賬戶名/Library/Developer/CoreSimulator/Devices/FDABA415-C26F-483F-B0F8-CDC02A03054B/data/Containers/Data/Application/85D2E7FC-F389-40D3-9607-03718564AFD3/Documents/Download/9f344d0e79cbceceed64ecaa62adf709/9f344d0e79cbceceed64ecaa62adf709.mp4 調(diào)用方式即: ffmpeg -ss 00:00:00 -i 源文件.ts文件路徑 -b:v 2000K -y 目標(biāo).mp4文件路徑 存在一個(gè)問題,我轉(zhuǎn)碼出來,,發(fā)現(xiàn).mp4視頻文件出奇的大,,724MB,而.ts源文件才70MB左右,,如果我把培訓(xùn)班的視頻都下載下來了,,那占用手機(jī)空間就會(huì)很大,我用的手機(jī)是128GB,,剩余空間也就不到15GB ,。 我把參數(shù)都去掉,用默認(rèn)的 ffmpge -i <in file> <output file> 轉(zhuǎn)碼出來是180MB左右,,不同命令參數(shù)轉(zhuǎn)碼出來的文件大小相差很大,,這涉及到對(duì)ffmpeg的理解和一些音視頻的概念基礎(chǔ)知識(shí)了。
關(guān)于.ts轉(zhuǎn).mp4的各種命令也很多,,我上網(wǎng)找到如下命令行: 覆蓋目標(biāo)文件,,使用h264_mp4toannexb,使用源文件聲音編解碼器,視頻編解碼器 ffmpeg -y -i < file> -vcodec copy -acodec copy -vbsf h264_mp4toannexb <output file> -ss :: -i < file> -b:v 2000K -y <output file> -y -i < file> -c:v libx264 -c:a copy -bsf:a aac_adtstoasc <output file>
iOS里使用ffmpeg命令行的代碼,,涉及到一些objective-c調(diào)c語(yǔ)言的方式,,分享下 //輸入源文件沙盒路徑 char* input=(char*) [inputPath UTF8String]; //輸出目標(biāo)文件沙盒路徑 char* output= (char*)[outpath UTF8String]; //ffmpeg命令 char* a[]={"ffmpeg","-y","-i",input,output}; //開始調(diào)起ffmpeg ffmpeg_main(sizeof(a)/sizeof(*a),a);
基本概念
1.視頻和視頻格式的說明 視頻是N多張圖片的集合,播放視頻本職其實(shí)就是連續(xù)播放"很多"張圖片,,其播放每張圖片的時(shí)間間隔非常的短,,我們把每一張圖片稱為每一幀,每一幀圖片有多少像素,,稱為這張圖像的分辨率,,比如我們有一個(gè)1.mp4視頻文件,它的分辨率就是每一幀圖像的分辨率,; 拿到一個(gè)視頻文件,我們除了知道常見的分辨率和封裝格式外,,諸如幀率,,碼率最好也了解一下,; 幀率說明視頻單位時(shí)間1秒內(nèi)可以播放多少?gòu)垐D像,視頻的幀率可以是恒定的,,也可以是動(dòng)態(tài)的,,碼率說明單位時(shí)間內(nèi)記錄視頻數(shù)據(jù)的總量,比如一個(gè)24分鐘,,900MB的視 頻,,其碼率為7200MBit/1440s=5000Kbps=5Mbps,單位一般是kbit/s或者M(jìn)bit/s,一個(gè)視頻里不僅有圖像還有音頻,,碼率是兩者的總和,,反過來通過碼率,我們也很容易知道一個(gè)視頻文件的大小,,其計(jì)算公式為(音頻編碼率+視頻編碼率)kbps/8*視頻長(zhǎng)度s 我們知道一張圖片如果不編碼,,其占用字節(jié)空間很大,比如一張圖片像素為1024*1024,,位深為32位,,則圖片占用空間為: 1024*1024*32/8=4096KB ,如果一個(gè)視頻單純的把所有圖片占用的空間加起來,那么其占用空間會(huì)出奇的大,,這樣對(duì)于存儲(chǔ)和傳輸都是很大問題的,。 我們?nèi)粘?吹降?mp4后綴的視頻文件都是把視頻流,,音頻流編碼后通過mp4格式封裝好的文 件,,看起來文件占用并不大,mp4是一種視頻封裝格式,,我們還會(huì)看見諸如.mkv,.avi等視頻封裝格式,。 前面說到,需要對(duì)視頻流數(shù)據(jù)進(jìn)行編碼,,減少占用空間,,最著名的視頻編碼格式就是h.264了.
2.視頻是如何被播放出來的? 本app對(duì).mp4文件進(jìn)行離線播放,,原理是如何的,,我是直接拿"輪子"搭的,這個(gè)輪子是SJVideoPlayer ,其內(nèi)部對(duì)視頻播放的原理直接使用的是蘋果官方AVPlayer,在其基礎(chǔ)上進(jìn)行封裝的,,這里不對(duì)封裝做研究,,了解下其AVPlayer能做什么? 之前在做廣告業(yè)務(wù)的時(shí)候,視頻這一塊也是用這個(gè)處理的,,受限于業(yè)務(wù)需要,,使用還不夠深,這里對(duì)SJVideoPlayer分析,,了解下其的強(qiáng)大之處,! a. 加載本地視頻文件,,進(jìn)行開頭和定點(diǎn)開始播放,任意構(gòu)建自定義播放尺寸,,添加到UIView上,。 b. 支持續(xù)播和旋轉(zhuǎn),旋轉(zhuǎn)固定 c. 常規(guī)播放控制
站在巨人的肩膀上太久,,高處未免不勝寒,,有點(diǎn)發(fā)慌,來了解下底層播放的知識(shí)先! 拿到一個(gè)mp4視頻文件,,對(duì)其進(jìn)行播放,,需要一個(gè)解碼的過程,跟圖片一樣,,都需要解碼數(shù)據(jù),。大致的播放流程如下:
3.HLS(HTTP直播) HLS是蘋果的動(dòng)態(tài)碼率自適應(yīng)技術(shù),基于HTTP的流媒體網(wǎng)絡(luò)傳輸協(xié)議,它的工作原理是把整個(gè)流分成一個(gè)個(gè)小的基于HTTP的文件來下載,,每次只下載一些,。當(dāng)媒體流正在播放時(shí),客戶端可以選擇從許多不同的備用源中以不同的速率下載同樣的資源,,允許流媒體會(huì)話適應(yīng)不同的數(shù)據(jù)速率,。支持的視頻流編碼為H.264。我們?cè)谝曨l網(wǎng)站上看到的M3U8后綴的播放鏈接就是使用HLS協(xié)議的視頻,,它包括一個(gè)m3u8的索引文件,,TS媒體分片和key加密串文件(該加密串文件可有可無)。 HLS具有下述優(yōu)點(diǎn): a.用戶可以看完一段緩存一段,,防止只看一段視頻但是把整個(gè)視頻文件都緩存下來,,減少服務(wù)器壓力和流量消耗。 (這里讓我想到之前在做廣告業(yè)務(wù)的時(shí)候,,在視頻廣告這塊,,跟市面上很多廣告SDK都類似,使用的是.mp4視頻格式,,其實(shí)是難滿足這種場(chǎng)景的,,如果被聚合在一起的時(shí)候,各家的視頻緩存效率就是一項(xiàng)可PK的點(diǎn),,后續(xù)可思考這一塊~) 補(bǔ)充一下: HLS可以做加密保證文件的安全性和防止被盜用 1. 常見的一種是防盜鏈(嚴(yán)格來講這不屬于加密) ,, 也就是說給 m3u8 和 ts 文件的url動(dòng)態(tài)生成一個(gè) token , 比如這個(gè): 加密: https://blog.csdn.net/cnhome/article/details/73250495 解密: https://blog.csdn.net/sbdx/article/details/80595094
視頻加密方案: https://blog.csdn.net/ai2000ai/article/details/83106101
b.根據(jù)網(wǎng)絡(luò)帶寬切換不同的碼率,,兼顧速度和清晰度。
4.m3u8是個(gè)啥? 抓包分析請(qǐng)求鏈接,,是以后綴.M3U8 Content-Type是“application/vnd.apple.mpegurl”的文件
其文件內(nèi)容為.ts片段列表,,每一個(gè).ts片段對(duì)應(yīng)視頻流的不同數(shù)據(jù),全部.ts片段組成了視頻數(shù)據(jù),,客戶端不斷的去下載里面的片段,,由于片段之間的分段間隔非常短,最后看起來就是一條完整的播放流,,也就是正在播放的視頻,。 學(xué)員的手機(jī)以類似輪詢的方式不斷重復(fù)加載該.m3u8文件并將.ts片段追加實(shí)現(xiàn)流媒體的播放。 對(duì).m3u8文件內(nèi)容了解下: #EXT-X-TARGETDURATION: 表示每一個(gè).ts片段的最大時(shí)長(zhǎng) #EXTINF: 表示下面.ts片段的時(shí)長(zhǎng) #EXT-X-VERSION:表示該播放列表所兼容的所有版本 #EXT-X-ENDLIST:表明m3u8文件的結(jié)束 還有一些我示例里沒公布的標(biāo)記說明可參照官方說明: https://tools./html/draft-pantos-http-live-streaming-06,,這里有一篇中文翻譯版本: https://www.cnblogs.com/shakin/p/3870442.html 我抓包的視頻是已經(jīng)錄制好的視頻,,該播放列表的數(shù)據(jù)內(nèi)容格式是單碼率視頻適配流,而有時(shí)候不同用戶的網(wǎng)絡(luò)帶寬不一樣,,下發(fā)的.m3u8文件里的內(nèi)容可能有不同碼率的文件,,用戶手機(jī)會(huì)選擇一個(gè)適合自己的文件進(jìn)行播放,這樣來保證直播視頻流的流暢,; 多碼率視頻流會(huì)多出下面標(biāo)記: #EXT-X-STREAM-INF: 表示播放列表中的下一個(gè)url file,來標(biāo)識(shí)另一個(gè)播放列表文件 該標(biāo)記包含以下屬性 BANDWIDTH 指定碼率,,用于不同網(wǎng)絡(luò)帶寬自適應(yīng)選擇對(duì)應(yīng)的碼流播放 PROGRAM-ID 唯一ID CODECS 指定流的編碼類型 示例如下: #EXTM3U #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1280000 http:///1.m3u8 #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1280000 http:///2.m3u8 客戶端會(huì)首選選擇碼率最高的.m3u8請(qǐng)求
單碼率的如下: #EXTM3U #EXT-X-TARGETDURATION:5220 #EXTINF:5220, http://media./1.ts #EXT-X-ENDLIST
我這里碰到的.m3u8全部都是沒有加密過的,加密的一般會(huì)多出下圖紅框里的內(nèi)容
以后找時(shí)間看此類視頻如何處理吧。
5. FFMPEG FFmpeg是一個(gè)跨平臺(tái)的視音頻錄制,、轉(zhuǎn)換和流媒體化的解決方案, 這里需要用到視頻格式轉(zhuǎn)換的功能,。 FFMPEG在iOS平臺(tái)的使用,網(wǎng)上文章也很多,,這里說下怎么使用這個(gè)"輪子",,輪子的一些概念就不說了。 對(duì)一些靜態(tài)庫(kù)做下說明 libavformat:用于各種音視頻封裝格式的生成和解析,。 在iOS上使用ffmpeg庫(kù)進(jìn)行視頻相關(guān)操作. 一般用的是命令行方式,,命令行涉及參數(shù)很多,編碼前參考官方文檔和網(wǎng)上文章,,在自己的Mac電腦上開啟終端命令,,建議手動(dòng)測(cè)試一遍對(duì)應(yīng)的命令。 第一步, 先安裝ffmpeg brew install ffmpeg 第二步,,準(zhǔn)備好視頻相關(guān)文件,。 第三步, 測(cè)試開發(fā)場(chǎng)景命令行 格式說明:-i filename 輸入文件-t duration 設(shè)置處理時(shí)間-ss position 設(shè)置開始時(shí)間-b:v bitrate 設(shè)置視頻碼率 -b:a bitrate 設(shè)置音頻碼率-r fps 設(shè)置幀率-s wxh 設(shè)置幀大小,格式為WxH,例如640x480-c:v codec 設(shè)置視頻編碼器-c:a codec 設(shè)置音頻編碼器-ar freq 設(shè)置音頻采樣率 一些示例:-codec copy 強(qiáng)制使用codec編解碼方式,copy表示不進(jìn)行重新編碼-vcodec copy -acodec copy 跟上面一樣-vn 取消視頻輸出-an 取消音頻輸出-bsf:v h264_mp4toannexb 視頻數(shù)據(jù)使用h264_mp4toannexb這個(gè)bitstream filter來轉(zhuǎn)換為原始的H264數(shù)據(jù)-f mp4 指定輸出格式為mp4 分享一些示例,也歡迎大家留言一起探討 ! -ss :: -t :: -i input.mp4 -vcodec copy - ffmpeg -i input.mp4 -vn -acodec copy output.m4a -vn 取消視頻輸出 -acodec 指定音頻編碼 copy代表不進(jìn)行重新編碼 提取一個(gè)視頻文件中的音頻文件 ffmpeg -i input.mp4 -an -vcodec copy output.mp4 -an 取消音頻輸出 -vcodec 指定視頻編碼 copy代表不進(jìn)行重新編碼 使一個(gè)視頻中的音頻靜音,只保留視頻 ffmpeg -i output.mp4 -an -vcodec copy -bsf:v h264_mp4toannexb output.h264 視頻數(shù)據(jù)使用h264_mp4toannexb這個(gè)bitstream filter來轉(zhuǎn)換為原始的H264數(shù)據(jù)。 從mp4文件中抽取視頻流導(dǎo)出為裸H264數(shù)據(jù) ffmpeg -i test.aac -i test.h264 -acodec copy -bsf:a aac_adtstoasc -vcodec copy -f mp4 output.mp4 -f 指定輸出格式 使用aac音頻數(shù)據(jù)和H264的視頻生成MP4文件 ffmpeg -i input.wav -acodec libfdk_aac output.aac 對(duì)音頻文件的編碼格式做轉(zhuǎn)換 ffmpeg -i input.wav -acodec pcm_s16le -f s16le output.pcm 從wav音頻文件中導(dǎo)出pcm裸數(shù)據(jù) ffmpeg -i input.flv -vcodec libx264 -acodec copy output.mp4 重新編碼視頻文件,,復(fù)制音頻流 同時(shí)封裝到MP4格式文件中 ffmpeg -i input.mp4 -vf scale=100:-1 -t 5 -r 10 image.gif -vf設(shè)置視頻的過濾器 按照分辨率比例不動(dòng)寬度改為100(使用videofilter的scalefilter),,幀率改為10(-r)時(shí)長(zhǎng)改為5(-t) 將一個(gè)MP4格式的視頻轉(zhuǎn)換為gif格式的動(dòng)圖 ffmpeg -i input.mp4 -r 0.25 frames_%04d.png 每4 秒截取一幀視頻生成一張圖片,,生成的圖片從frames_0001.png開始遞增 ffmpeg -i frames_%04d.png -r 5 output.gif 使用一組圖片可以組成一個(gè)gif ffmpeg -i input.wav -af 'volume=0.5’ output.wav 使用音量效果器,,改變一個(gè)音頻媒體文件中的音量 ffmpeg -i input.wav -filter_complex afade=t=in:ss=0:d=5 output.wav 將該音頻文件前5秒鐘做一個(gè)淡入效果 ffmpeg -i input.wav -filter_complex afade=t=out:st=200:d=5 output.wav 將音頻從200s開始 做5秒的淡出效果 ffmpeg -i input1.wav -i input2.wav -filter_complex amix=inputs=2:duration=shortest output.wav 將兩個(gè)音頻進(jìn)行合并,,按照時(shí)間較短的音頻時(shí)間長(zhǎng)度作為輸出 ffmpeg -i input.wav -filter_complex atempo=0.5 output.wav 將音頻按照0.5倍的速度進(jìn)行處理生成output.wav 時(shí)間長(zhǎng)度變?yōu)樵瓉淼?倍,音高不變 ffmpeg -i test.avi -i frames_0004.jpeg -filter_complex overlay after.avi 給視頻添加水印 ffmpeg -i test.avi -vf"drawtext=fontfile=simhei.ttf:text='雷’:x=100:y=10:fontsize=24:fontcolor=yellow:shadowy=2" after.avi 添加文字水印 ffmpeg -i input.flv -c:v libx264 -b:v 800k -c:a libfdk_aac -vf eq=brightness=0.25 -f mp4 output.mp4 視頻提高亮度 參數(shù)brightness 取值范圍-1.0到1.0。 ffmpeg -i input.flv -c:v libx264 -b:v 800k -c:a libfdk_aac -vf eq=contrast=1.5 -f mp4 output.mp4 視頻增加對(duì)比度 參數(shù)contrast,,取值范圍是從-2.0到2.0 ffmpeg -i input.mp4 -vf “transpose=1” -b:v 600k output.mp4 視頻旋轉(zhuǎn)效果器使用 ffmpeg -i input.mp4 -an -vf “crop=240:480:120:0” -vcodec libx264 -b:v 600k output.mp4 視頻裁剪效果器使用 ffmpeg -f rawvideo -pix_fmt rgba -s 480*480 -i texture.rgb -f image2 -vcodec mjpeg output.jpg 將一張RGBA格式表示的數(shù)據(jù)轉(zhuǎn)換為JPEG格式的圖片 ffmpeg -f rawvideo -pix_fmt yuv420p -s 480*480 -i texture.yuv -f image2 -vcodec mjpeg output.jpg 將一個(gè)YUV格式表示的數(shù)據(jù)轉(zhuǎn)換為JPEG格式的圖片 ffmpeg -re -i ipnut.mp4 -acodec copy -vcodec copy -f flv rtmp://xxx 將一段視頻推送到流媒體服務(wù)器上 ffmpeg -i http://xxx/xxx.flv -acodec copy -vcodec copy -f flv output.flv 將流媒體服務(wù)器上的流dump到本地
額外分享ffprobe和ffplay的一些命令輔助測(cè)試. ffprobe常用命令 ffprobe 文件名 查看音頻視頻文件信息 ffprobe -show_format 文件名 查看文件的輸出格式信息,,時(shí)間長(zhǎng)度,文件大小,,比特率,,流數(shù)目等。 ffprobe -print_format json -show_streams 文件名 以json格式輸出詳細(xì)信息 ffprobe -show_frames 文件名 顯示幀信息 ffprobe -show_packets 文件名 查看包信息 ffplay常用命令 ffplay 文件名 播放音頻,、視頻文件 ffplay 文件名 -loop 10循環(huán)播放文件10次 ffplay 文件名 -ast 1播放視頻第一路音頻流 參數(shù)如果是2 就是第二路音頻流 如果沒有就會(huì)靜音,。 ffplay 文件名 -vst 1播放視頻第一路視頻流 參數(shù)如果是2 就是第二路視頻流 沒有顯示黑屏 ffplay .pcm文件 -f 格式 -channels 2 聲道數(shù) -ar 采樣率 播放pcm文件 必須設(shè)置參數(shù)正確 ffplay -f rawvideo -pixel_format yuv420p -s 480480 .yuv文件-f rawvideo 代表原始格式-pixel_format yuv420p 表示格式-s 480480 寬高 ffplay -f rawvideo -pixel_format rgb24 -s 480*480 .rgb文件 播放rgb原始數(shù)據(jù) ffplay 文件名 -sync audio 指定ffplay使用音頻為基準(zhǔn)進(jìn)行音視頻同步默認(rèn)ffplay也是這樣 ffplay 文件名 -sync video 指定ffplay使用視頻為基準(zhǔn)進(jìn)行音視頻同步 ffplay 文件名 -ext video 指定ffplay使用外部時(shí)鐘為基準(zhǔn)進(jìn)行音視頻同步 ffmpeg官方調(diào)用文檔: https:///documentation.html,可查看相關(guān)參數(shù)使用
上面是用命令行方式的調(diào)用,,需要額外自己編譯ffmpeg對(duì)應(yīng)版本的命令行靜態(tài)庫(kù),,才能在iOS里方便調(diào)用,除此以外還可以使用api方式的代碼調(diào)用 在官網(wǎng)上http:///documentation.html 可以找到對(duì)應(yīng)版本的api接口文檔,。 |
|