背景: 去年下半年由于種種因素驅(qū)動下,準(zhǔn)備去考研,在之前同事的推薦下,參加了考研培訓(xùn)班,培訓(xùn)班發(fā)了紙質(zhì)書籍和線上視頻觀看賬號,由于線上視頻需要全程聯(lián)網(wǎng)才能觀看,突發(fā)奇想,要是我把這些視頻下載下來,沒網(wǎng)的時候也能拿出來觀看復(fù)習(xí)多好; 在此背景下,花了幾天時間簡單做了一個app出來輔助復(fù)習(xí),前段時間通過了考研復(fù)試,這幾天利用空閑時間,完善了app功能,同時記錄下開發(fā)過程和一些感悟; 效果:
功能簡單介紹: 在線播放(下載).m3u8視頻,PC瀏覽器查看和離線觀看! 注意: 由于涉及到培訓(xùn)班視頻的隱私問題,這里不給出視頻播放畫面,敬請諒解!
如何實現(xiàn)? 市面上眾多培訓(xùn)班的視頻一般都不是普通的.mp4視頻,很多都是經(jīng)過了加密或者非專業(yè)人士知道的特殊視頻格式, 工作期間經(jīng)常使用抓包工具,我想先試下抓包看看,說不定可以了解到一些信息,從而啟發(fā)下一步,好,說干就干....
在用charles抓包的時候,發(fā)現(xiàn)截圖該域名下不斷的有新請求產(chǎn)生,有一個.M3U8請求地址,還有不斷產(chǎn)生的.ts.ts請求地址, 網(wǎng)上搜索相關(guān)文章,茅塞頓開,實現(xiàn)起來并不復(fù)雜。 涉及到的技術(shù)實現(xiàn)網(wǎng)上文章很多,當(dāng)時備考時間短,我把自己的核心需求理了下,網(wǎng)上找相關(guān)"輪子",有滿足自己的需要就直接拿來組裝了 ,哈哈
技術(shù)要點(diǎn) 一.iOS音視頻播放 iOS音視頻播放技術(shù)現(xiàn)在很成熟了,之前在項目中有涉及到視頻播放相關(guān)處理的功能,代碼中主要使用的類是AVPlayer,個人建議首先看蘋果文檔,最全面最權(quán)威的了,網(wǎng)上找到的不夠全面,要綜合不同文章才能全面些,打開位置Xcode-Window-Developer Documentation,搜索類名
官方說明該類可用來處理本地和遠(yuǎn)程基于文件的媒體播放,例如quiktime電影,mp3音頻文件,以及使用HLS直播提供的視聽媒體; 使用該對象就可以直接播放前面說的.m3u8文件了; 不過這樣會存在一個問題,當(dāng)網(wǎng)絡(luò)不好的時候,體驗就很糟糕了,而且也不能離線觀看; 看能不能處理成常用的.mp4文件,方便保存和離線觀看? 基本思路是: 批量下載.ts.ts文件=》合并文件=>轉(zhuǎn)碼成.mp4 批量下載ts.ts文件就基本的文件網(wǎng)絡(luò)下載思路了 合并文件: 用一個NSMutableData來存儲合并后的文件數(shù)據(jù),循環(huán)讀取每一個.ts片段數(shù)據(jù),追加到NSMutableData 轉(zhuǎn)碼成.mp4: 這一步需要用到FFmpeg框架來做,對應(yīng)的iOS庫,一般可采用命令行的方式進(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文件路徑 存在一個問題,我轉(zhuǎn)碼出來,發(fā)現(xiàn).mp4視頻文件出奇的大,724MB,而.ts源文件才70MB左右,如果我把培訓(xùn)班的視頻都下載下來了,那占用手機(jī)空間就會很大,我用的手機(jī)是128GB,剩余空間也就不到15GB 。 我把參數(shù)都去掉,用默認(rèn)的 ffmpge -i <in file> <output file> 轉(zhuǎn)碼出來是180MB左右,不同命令參數(shù)轉(zhuǎn)碼出來的文件大小相差很大,這涉及到對ffmpeg的理解和一些音視頻的概念基礎(chǔ)知識了。
關(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語言的方式,分享下 //輸入源文件沙盒路徑 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多張圖片的集合,播放視頻本職其實就是連續(xù)播放"很多"張圖片,其播放每張圖片的時間間隔非常的短,我們把每一張圖片稱為每一幀,每一幀圖片有多少像素,稱為這張圖像的分辨率,比如我們有一個1.mp4視頻文件,它的分辨率就是每一幀圖像的分辨率; 拿到一個視頻文件,我們除了知道常見的分辨率和封裝格式外,諸如幀率,碼率最好也了解一下; 幀率說明視頻單位時間1秒內(nèi)可以播放多少張圖像,視頻的幀率可以是恒定的,也可以是動態(tài)的,碼率說明單位時間內(nèi)記錄視頻數(shù)據(jù)的總量,比如一個24分鐘,900MB的視 頻,其碼率為7200MBit/1440s=5000Kbps=5Mbps,單位一般是kbit/s或者M(jìn)bit/s,一個視頻里不僅有圖像還有音頻,碼率是兩者的總和,反過來通過碼率,我們也很容易知道一個視頻文件的大小,其計算公式為(音頻編碼率+視頻編碼率)kbps/8*視頻長度s 我們知道一張圖片如果不編碼,其占用字節(jié)空間很大,比如一張圖片像素為1024*1024,位深為32位,則圖片占用空間為: 1024*1024*32/8=4096KB ,如果一個視頻單純的把所有圖片占用的空間加起來,那么其占用空間會出奇的大,這樣對于存儲和傳輸都是很大問題的。 我們?nèi)粘?吹降?mp4后綴的視頻文件都是把視頻流,音頻流編碼后通過mp4格式封裝好的文 件,看起來文件占用并不大,mp4是一種視頻封裝格式,我們還會看見諸如.mkv,.avi等視頻封裝格式。 前面說到,需要對視頻流數(shù)據(jù)進(jìn)行編碼,減少占用空間,最著名的視頻編碼格式就是h.264了.
2.視頻是如何被播放出來的? 本app對.mp4文件進(jìn)行離線播放,原理是如何的,我是直接拿"輪子"搭的,這個輪子是SJVideoPlayer ,其內(nèi)部對視頻播放的原理直接使用的是蘋果官方AVPlayer,在其基礎(chǔ)上進(jìn)行封裝的,這里不對封裝做研究,了解下其AVPlayer能做什么? 之前在做廣告業(yè)務(wù)的時候,視頻這一塊也是用這個處理的,受限于業(yè)務(wù)需要,使用還不夠深,這里對SJVideoPlayer分析,了解下其的強(qiáng)大之處! a. 加載本地視頻文件,進(jìn)行開頭和定點(diǎn)開始播放,任意構(gòu)建自定義播放尺寸,添加到UIView上。 b. 支持續(xù)播和旋轉(zhuǎn),旋轉(zhuǎn)固定 c. 常規(guī)播放控制
站在巨人的肩膀上太久,高處未免不勝寒,有點(diǎn)發(fā)慌,來了解下底層播放的知識先! 拿到一個mp4視頻文件,對其進(jìn)行播放,需要一個解碼的過程,跟圖片一樣,都需要解碼數(shù)據(jù)。大致的播放流程如下:
3.HLS(HTTP直播) HLS是蘋果的動態(tài)碼率自適應(yīng)技術(shù),基于HTTP的流媒體網(wǎng)絡(luò)傳輸協(xié)議,它的工作原理是把整個流分成一個個小的基于HTTP的文件來下載,每次只下載一些。當(dāng)媒體流正在播放時,客戶端可以選擇從許多不同的備用源中以不同的速率下載同樣的資源,允許流媒體會話適應(yīng)不同的數(shù)據(jù)速率。支持的視頻流編碼為H.264。我們在視頻網(wǎng)站上看到的M3U8后綴的播放鏈接就是使用HLS協(xié)議的視頻,它包括一個m3u8的索引文件,TS媒體分片和key加密串文件(該加密串文件可有可無)。 HLS具有下述優(yōu)點(diǎn): a.用戶可以看完一段緩存一段,防止只看一段視頻但是把整個視頻文件都緩存下來,減少服務(wù)器壓力和流量消耗。 (這里讓我想到之前在做廣告業(yè)務(wù)的時候,在視頻廣告這塊,跟市面上很多廣告SDK都類似,使用的是.mp4視頻格式,其實是難滿足這種場景的,如果被聚合在一起的時候,各家的視頻緩存效率就是一項可PK的點(diǎn),后續(xù)可思考這一塊~) 補(bǔ)充一下: HLS可以做加密保證文件的安全性和防止被盜用 1. 常見的一種是防盜鏈(嚴(yán)格來講這不屬于加密) , 也就是說給 m3u8 和 ts 文件的url動態(tài)生成一個 token , 比如這個: 加密: 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是個啥? 抓包分析請求鏈接,是以后綴.M3U8 Content-Type是“application/vnd.apple.mpegurl”的文件
其文件內(nèi)容為.ts片段列表,每一個.ts片段對應(yīng)視頻流的不同數(shù)據(jù),全部.ts片段組成了視頻數(shù)據(jù),客戶端不斷的去下載里面的片段,由于片段之間的分段間隔非常短,最后看起來就是一條完整的播放流,也就是正在播放的視頻。 學(xué)員的手機(jī)以類似輪詢的方式不斷重復(fù)加載該.m3u8文件并將.ts片段追加實現(xiàn)流媒體的播放。 對.m3u8文件內(nèi)容了解下: #EXT-X-TARGETDURATION: 表示每一個.ts片段的最大時長 #EXTINF: 表示下面.ts片段的時長 #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)容格式是單碼率視頻適配流,而有時候不同用戶的網(wǎng)絡(luò)帶寬不一樣,下發(fā)的.m3u8文件里的內(nèi)容可能有不同碼率的文件,用戶手機(jī)會選擇一個適合自己的文件進(jìn)行播放,這樣來保證直播視頻流的流暢; 多碼率視頻流會多出下面標(biāo)記: #EXT-X-STREAM-INF: 表示播放列表中的下一個url file,來標(biāo)識另一個播放列表文件 該標(biāo)記包含以下屬性 BANDWIDTH 指定碼率,用于不同網(wǎng)絡(luò)帶寬自適應(yīng)選擇對應(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 客戶端會首選選擇碼率最高的.m3u8請求
單碼率的如下: #EXTM3U #EXT-X-TARGETDURATION:5220 #EXTINF:5220, http://media./1.ts #EXT-X-ENDLIST
我這里碰到的.m3u8全部都是沒有加密過的,加密的一般會多出下圖紅框里的內(nèi)容 以后找時間看此類視頻如何處理吧。
5. FFMPEG FFmpeg是一個跨平臺的視音頻錄制、轉(zhuǎn)換和流媒體化的解決方案, 這里需要用到視頻格式轉(zhuǎn)換的功能。 FFMPEG在iOS平臺的使用,網(wǎng)上文章也很多,這里說下怎么使用這個"輪子",輪子的一些概念就不說了。 對一些靜態(tài)庫做下說明 libavformat:用于各種音視頻封裝格式的生成和解析。 在iOS上使用ffmpeg庫進(jìn)行視頻相關(guān)操作. 一般用的是命令行方式,命令行涉及參數(shù)很多,編碼前參考官方文檔和網(wǎng)上文章,在自己的Mac電腦上開啟終端命令,建議手動測試一遍對應(yīng)的命令。 第一步, 先安裝ffmpeg brew install ffmpeg 第二步,準(zhǔn)備好視頻相關(guān)文件。 第三步, 測試開發(fā)場景命令行 格式說明:-i filename 輸入文件-t duration 設(shè)置處理時間-ss position 設(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這個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)行重新編碼 提取一個視頻文件中的音頻文件 ffmpeg -i input.mp4 -an -vcodec copy output.mp4 -an 取消音頻輸出 -vcodec 指定視頻編碼 copy代表不進(jìn)行重新編碼 使一個視頻中的音頻靜音,只保留視頻 ffmpeg -i output.mp4 -an -vcodec copy -bsf:v h264_mp4toannexb output.h264 視頻數(shù)據(jù)使用h264_mp4toannexb這個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 對音頻文件的編碼格式做轉(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ù)制音頻流 同時封裝到MP4格式文件中 ffmpeg -i input.mp4 -vf scale=100:-1 -t 5 -r 10 image.gif -vf設(shè)置視頻的過濾器 按照分辨率比例不動寬度改為100(使用videofilter的scalefilter),幀率改為10(-r)時長改為5(-t) 將一個MP4格式的視頻轉(zhuǎn)換為gif格式的動圖 ffmpeg -i input.mp4 -r 0.25 frames_%04d.png 每4 秒截取一幀視頻生成一張圖片,生成的圖片從frames_0001.png開始遞增 ffmpeg -i frames_%04d.png -r 5 output.gif 使用一組圖片可以組成一個gif ffmpeg -i input.wav -af 'volume=0.5’ output.wav 使用音量效果器,改變一個音頻媒體文件中的音量 ffmpeg -i input.wav -filter_complex afade=t=in:ss=0:d=5 output.wav 將該音頻文件前5秒鐘做一個淡入效果 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 將兩個音頻進(jìn)行合并,按照時間較短的音頻時間長度作為輸出 ffmpeg -i input.wav -filter_complex atempo=0.5 output.wav 將音頻按照0.5倍的速度進(jìn)行處理生成output.wav 時間長度變?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 視頻增加對比度 參數(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 將一個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的一些命令輔助測試. ffprobe常用命令 ffprobe 文件名 查看音頻視頻文件信息 ffprobe -show_format 文件名 查看文件的輸出格式信息,時間長度,文件大小,比特率,流數(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 就是第二路音頻流 如果沒有就會靜音。 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使用外部時鐘為基準(zhǔn)進(jìn)行音視頻同步 ffmpeg官方調(diào)用文檔: https:///documentation.html,可查看相關(guān)參數(shù)使用
上面是用命令行方式的調(diào)用,需要額外自己編譯ffmpeg對應(yīng)版本的命令行靜態(tài)庫,才能在iOS里方便調(diào)用,除此以外還可以使用api方式的代碼調(diào)用 在官網(wǎng)上http:///documentation.html 可以找到對應(yīng)版本的api接口文檔。 |
|