背景: 去年下半年因爲種種因素驅動下,準備去考研,在以前同事的推薦下,參加了考研培訓班,培訓班發了紙質書籍和線上視頻觀看帳號,因爲線上視頻須要全程聯網才能觀看,突發奇想,要是我把這些視頻下載下來,沒網的時候也能拿出來觀看複習多好; 在此背景下,花了幾天時間簡單作了一個app出來輔助複習,前段時間經過了考研複試,這幾天利用空閒時間,完善了app功能,同時記錄下開發過程和一些感悟;html
效果:git
功能簡單介紹: 在線播放(下載).m3u8視頻,PC瀏覽器查看和離線觀看!github
注意: 因爲涉及到培訓班視頻的隱私問題,這裏不給出視頻播放畫面,敬請諒解!
objective-c
如何實現?json
市面上衆多培訓班的視頻通常都不是普通的.mp4視頻,不少都是通過了加密或者非專業人士知道的特殊視頻格式, 工做期間常用抓包工具,我想先試下抓包看看,說不定能夠了解到一些信息,從而啓發下一步,好,說幹就幹....api
在用charles抓包的時候,發現截圖該域名下不斷的有新請求產生,有一個.M3U8請求地址,還有不斷產生的.ts.ts請求地址, 網上搜索相關文章,茅塞頓開,實現起來並不複雜。 瀏覽器
涉及到的技術實現網上文章不少,當時備考時間短,我把本身的核心需求理了下,網上找相關"輪子",有知足本身的須要就直接拿來組裝了 ,哈哈 緩存
技術要點安全
一.iOS音視頻播放服務器
iOS音視頻播放技術如今很成熟了,以前在項目中有涉及到視頻播放相關處理的功能,代碼中主要使用的類是AVPlayer,我的建議首先看蘋果文檔,最全面最權威的了,網上找到的不夠全面,要綜合不一樣文章才能全面些,打開位置Xcode-Window-Developer Documentation,搜索類名
官方說明該類可用來處理本地和遠程基於文件的媒體播放,例如quiktime電影,mp3音頻文件,以及使用HLS直播提供的視聽媒體; 使用該對象就能夠直接播放前面說的.m3u8文件了;
不過這樣會存在一個問題,當網絡很差的時候,體驗就很糟糕了,並且也不能離線觀看; 看能不能處理成經常使用的.mp4文件,方便保存和離線觀看?
基本思路是: 批量下載.ts.ts文件=》合併文件=>轉碼成.mp4
批量下載ts.ts文件就基本的文件網絡下載思路了
合併文件: 用一個NSMutableData來存儲合併後的文件數據,循環讀取每個.ts片斷數據,追加到NSMutableData
轉碼成.mp4: 這一步須要用到FFmpeg框架來作,對應的iOS庫,通常可採用命令行的方式進行調用,這裏的轉碼示例以下:
調用方式即:
ffmpeg -ss 00:00:00 -i 源文件.ts文件路徑 -b:v 2000K -y 目標.mp4文件路徑
存在一個問題,我轉碼出來,發現.mp4視頻文件出奇的大,724MB,而.ts源文件才70MB左右,若是我把培訓班的視頻都下載下來了,那佔用手機空間就會很大,我用的手機是128GB,剩餘空間也就不到15GB 。
我把參數都去掉,用默認的
ffmpge -i <in file> <output file>
轉碼出來是180MB左右,不一樣命令參數轉碼出來的文件大小相差很大,這涉及到對ffmpeg的理解和一些音視頻的概念基礎知識了。
關於.ts轉.mp4的各類命令也不少,我上網找到以下命令行:
覆蓋目標文件,使用h264_mp4toannexb,使用源文件聲音編解碼器,視頻編解碼器
ffmpeg -y -i <in file> -vcodec copy -acodec copy -vbsf h264_mp4toannexb <output file>
源文件開頭到末尾,目標文件視頻的碼率設置爲2000kbps ffmpeg -ss 00:00:00 -i <in file> -b:v 2000K -y <output file>
ffmpeg -y -i <in file> -c:v libx264 -c:a copy -bsf:a aac_adtstoasc <output file>
iOS裏使用ffmpeg命令行的代碼,涉及到一些objective-c調c語言的方式,分享下
//輸入源文件沙盒路徑 char* input=(char*) [inputPath UTF8String]; //輸出目標文件沙盒路徑 char* output= (char*)[outpath UTF8String]; //ffmpeg命令 char* a[]={"ffmpeg","-y","-i",input,output}; //開始調起ffmpeg ffmpeg_main(sizeof(a)/sizeof(*a),a);
基本概念
1.視頻和視頻格式的說明
視頻是N多張圖片的集合,播放視頻本職其實就是連續播放"不少"張圖片,其播放每張圖片的時間間隔很是的短,咱們把每一張圖片稱爲每一幀,每一幀圖片有多少像素,稱爲這張圖像的分辨率,好比咱們有一個1.mp4視頻文件,它的分辨率就是每一幀圖像的分辨率;
拿到一個視頻文件,咱們除了知道常見的分辨率和封裝格式外,諸如幀率,碼率最好也瞭解一下; 幀率說明視頻單位時間1秒內能夠播放多少張圖像,視頻的幀率能夠是恆定的,也能夠是動態的,碼率說明單位時間內記錄視頻數據的總量,好比一個24分鐘,900MB的視
頻,其碼率爲7200MBit/1440s=5000Kbps=5Mbps,單位通常是kbit/s或者Mbit/s,一個視頻裏不只有圖像還有音頻,碼率是二者的總和,反過來經過碼率,咱們也很容易知道一個視頻文件的大小,其計算公式爲(音頻編碼率+視頻編碼率)kbps/8*視頻長度s
咱們知道一張圖片若是不編碼,其佔用字節空間很大,好比一張圖片像素爲1024*1024,位深爲32位,則圖片佔用空間爲: 1024*1024*32/8=4096KB ,若是一個視頻單純的把全部圖片佔用的空間加起來,那麼其佔用空間會出奇的大,這樣對於存儲和傳輸都是很大問題的。
咱們平常看到的.mp4後綴的視頻文件都是把視頻流,音頻流編碼後經過mp4格式封裝好的文
件,看起來文件佔用並不大,mp4是一種視頻封裝格式,咱們還會看見諸如.mkv,.avi等視頻封裝格式。
前面說到,須要對視頻流數據進行編碼,減小佔用空間,最著名的視頻編碼格式就是h.264了.
2.視頻是如何被播放出來的?
本app對.mp4文件進行離線播放,原理是如何的,我是直接拿"輪子"搭的,這個輪子是SJVideoPlayer ,其內部對視頻播放的原理直接使用的是蘋果官方AVPlayer,在其基礎上進行封裝的,這裏不對封裝作研究,瞭解下其AVPlayer能作什麼?
以前在作廣告業務的時候,視頻這一塊也是用這個處理的,受限於業務須要,使用還不夠深,這裏對SJVideoPlayer分析,瞭解下其的強大之處!
a. 加載本地視頻文件,進行開頭和定點開始播放,任意構建自定義播放尺寸,添加到UIView上。
b. 支持續播和旋轉,旋轉固定
c. 常規播放控制
站在巨人的肩膀上過久,高處未免不勝寒,有點發慌,來了解下底層播放的知識先!
拿到一個mp4視頻文件,對其進行播放,須要一個解碼的過程,跟圖片同樣,都須要解碼數據。大體的播放流程以下:
3.HLS(HTTP直播)
HLS是蘋果的動態碼率自適應技術,基於HTTP的流媒體網絡傳輸協議,它的工做原理是把整個流分紅一個個小的基於HTTP的文件來下載,每次只下載一些。當媒體流正在播放時,客戶端能夠選擇從許多不一樣的備用源中以不一樣的速率下載一樣的資源,容許流媒體會話適應不一樣的數據速率。支持的視頻流編碼爲H.264。咱們在視頻網站上看到的M3U8後綴的播放連接就是使用HLS協議的視頻,它包括一個m3u8的索引文件,TS媒體分片和key加密串文件(該加密串文件無關緊要)。
HLS具備下述優勢:
a.用戶能夠看完一段緩存一段,防止只看一段視頻可是把整個視頻文件都緩存下來,減小服務器壓力和流量消耗。 (這裏讓我想到以前在作廣告業務的時候,在視頻廣告這塊,跟市面上不少廣告SDK都相似,使用的是.mp4視頻格式,實際上是難知足這種場景的,若是被聚合在一塊兒的時候,各家的視頻緩存效率就是一項可PK的點,後續可思考這一塊~)
補充一下: HLS能夠作加密保證文件的安全性和防止被盜用
1. 常見的一種是防盜鏈(嚴格來說這不屬於加密) , 也就是說給 m3u8 和 ts 文件的url動態生成一個 token , 好比這個:
http://www.cuplayer.com/m3u8/hunan/desc.m3u8?stream_id=hunan<m=1410595018&lkey=8bc1e0fe35f6321ef560f8ccffb70e5d&path=59.49.42.14,58.59.3.9,58.59.3.51&platid=10&splatid=1015&tag=live&cips=127.0.0.1&ext=m3u8&sign=live_tv
這個url是隨着不少參數動態變化的,好比時間,用戶id、ip地址,內容id , 致使你沒法使用這個url盜鏈,這種方式能夠防止其餘網站直接使用你的url來觀看或者通常用戶的下載。
而ts文件的url 也須要加請求token , 會變化成相似 http://server/file.ts?token=xxxx 的方式, 這樣的話, ts文件的磁盤存儲位置不用變化,可是url是能夠變化的(能夠用query string方式,也能夠用 url rewrite 方式), 注意由於url是m3u8生成的,意味着m3u8文件是動態生成而並不是靜態文件
關於CDN緩存的問題, 首先m3u8文件確定不能緩存, 不然ts分片的動態url怎麼辦?
而後動態url的 ts分片CDN缺省是確定不緩存的,可是能夠稍微定製一下讓CDN忽略URL中的token部分。不少CDN都有本身的防盜鏈方案。
2. DRM加密。防盜鏈的方式是一種通常性的保護, 假如你想徹底保護你的內容,必須給ts內容加密, m3u8有這個tag: #EXT-X-KEY , 通常來講會提供一個url獲取加密key, 而後對ts片斷解密來播放文件
加密: 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.根據網絡帶寬切換不一樣的碼率,兼顧速度和清晰度。
4.m3u8是個啥?
抓包分析請求連接,是之後綴.M3U8 Content-Type是「application/vnd.apple.mpegurl」的文件
其文件內容爲.ts片斷列表,每個.ts片斷對應視頻流的不一樣數據,所有.ts片斷組成了視頻數據,客戶端不斷的去下載裏面的片斷,因爲片斷之間的分段間隔很是短,最後看起來就是一條完整的播放流,也就是正在播放的視頻。
學員的手機以相似輪詢的方式不斷重複加載該.m3u8文件並將.ts片斷追加實現流媒體的播放。
對.m3u8文件內容瞭解下:
#EXT-X-TARGETDURATION: 表示每個.ts片斷的最大時長
#EXTINF: 表示下面.ts片斷的時長
#EXT-X-VERSION:表示該播放列表所兼容的全部版本
#EXT-X-ENDLIST:代表m3u8文件的結束
還有一些我示例裏沒公佈的標記說明可參照官方說明: https://tools.ietf.org/html/draft-pantos-http-live-streaming-06,這裏有一篇中文翻譯版本: http://www.javashuo.com/article/p-oubidtlb-ht.html
我抓包的視頻是已經錄製好的視頻,該播放列表的數據內容格式是單碼率視頻適配流,而有時候不一樣用戶的網絡帶寬不同,下發的.m3u8文件裏的內容可能有不一樣碼率的文件,用戶手機會選擇一個適合本身的文件進行播放,這樣來保證直播視頻流的流暢; 多碼率視頻流會多出下面標記:
#EXT-X-STREAM-INF: 表示播放列表中的下一個url file,來標識另外一個播放列表文件
該標記包含如下屬性
BANDWIDTH 指定碼率,用於不一樣網絡帶寬自適應選擇對應的碼流播放
PROGRAM-ID 惟一ID
CODECS 指定流的編碼類型
示例以下:
#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1280000
http://example.com/1.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1280000
http://example.com/2.m3u8
客戶端會首選選擇碼率最高的.m3u8請求
單碼率的以下:
#EXTM3U
#EXT-X-TARGETDURATION:5220
#EXTINF:5220,
http://media.example.com/1.ts
#EXT-X-ENDLIST
我這裏碰到的.m3u8所有都是沒有加密過的,加密的通常會多出下圖紅框裏的內容
之後找時間看此類視頻如何處理吧。
5. FFMPEG
FFmpeg是一個跨平臺的視音頻錄製、轉換和流媒體化的解決方案, 這裏須要用到視頻格式轉換的功能。
FFMPEG在iOS平臺的使用,網上文章也不少,這裏說下怎麼使用這個"輪子",輪子的一些概念就不說了。
我在本身的mac pro上編譯iOS庫花了將近20分鐘,我把ffmpeg編譯好的文件放在百度網盤上:https://pan.baidu.com/s/1Xgtgku5IKxaaRrqAKEhrSQ 提取碼: egt2 ,方便你們直接下載使用。
對一些靜態庫作下說明
libavformat:用於各類音視頻封裝格式的生成和解析。
libavutil:核心工具庫,作一些基本音視頻處理操做。
libavcodec:音視頻各類格式的編解碼。
libswscale:圖像進行格式轉換模塊。
libavfilter:音視頻濾鏡庫。
libpostproc:後期處理模塊。
libswresample:用於音頻重採樣。
libadvice:用於硬件的音視頻採集。
libavresample:這個是老版本下編譯出來,新版本用libswresample代替
在iOS上使用ffmpeg庫進行視頻相關操做.
通常用的是命令行方式,命令行涉及參數不少,編碼前參考官方文檔和網上文章,在本身的Mac電腦上開啓終端命令,建議手動測試一遍對應的命令。
第一步, 先安裝ffmpeg
brew install ffmpeg
第二步,準備好視頻相關文件。
第三步, 測試開發場景命令行
格式說明: -i filename 輸入文件 -t duration 設置處理時間 -ss position 設置開始時間 -b:v bitrate 設置視頻碼率 -b:a bitrate 設置音頻碼率 -r fps 設置幀率 -s wxh 設置幀大小,格式爲WxH,例如640x480 -c:v codec 設置視頻編碼器 -c:a codec 設置音頻編碼器 -ar freq 設置音頻採樣率 一些示例: -codec copy 強制使用codec編解碼方式,copy表示不進行從新編碼 -vcodec copy -acodec copy 跟上面同樣 -vn 取消視頻輸出 -an 取消音頻輸出 -bsf:v h264_mp4toannexb 視頻數據使用h264_mp4toannexb這個bitstream filter來轉換爲原始的H264數據 -f mp4 指定輸出格式爲mp4
分享一些示例,也歡迎你們留言一塊兒探討 !
ffmpeg -ss 00:00:15 -t 00:00:05 -i input.mp4 -vcodec copy -acodec copy output.mp4
視頻剪切 從input.mp4的15秒開始,截取後5秒鐘的視頻,保存在output.mp4
ffmpeg -i input.mp4 -vn -acodec copy output.m4a
-vn 取消視頻輸出 -acodec 指定音頻編碼 copy表明不進行從新編碼 提取一個視頻文件中的音頻文件
ffmpeg -i input.mp4 -an -vcodec copy output.mp4
-an 取消音頻輸出 -vcodec 指定視頻編碼 copy表明不進行從新編碼 使一個視頻中的音頻靜音,只保留視頻
ffmpeg -i output.mp4 -an -vcodec copy -bsf:v h264_mp4toannexb output.h264
視頻數據使用h264_mp4toannexb這個bitstream filter來轉換爲原始的H264數據。 從mp4文件中抽取視頻流導出爲裸H264數據
ffmpeg -i test.aac -i test.h264 -acodec copy -bsf:a aac_adtstoasc -vcodec copy -f mp4 output.mp4
-f 指定輸出格式 使用aac音頻數據和H264的視頻生成MP4文件
ffmpeg -i input.wav -acodec libfdk_aac output.aac
對音頻文件的編碼格式作轉換
ffmpeg -i input.wav -acodec pcm_s16le -f s16le output.pcm
從wav音頻文件中導出pcm裸數據
ffmpeg -i input.flv -vcodec libx264 -acodec copy output.mp4
從新編碼視頻文件,複製音頻流 同時封裝到MP4格式文件中
ffmpeg -i input.mp4 -vf scale=100:-1 -t 5 -r 10 image.gif
-vf設置視頻的過濾器 按照分辨率比例不動寬度改成100(使用videofilter的scalefilter),幀率改成10(-r)時長改成5(-t) 將一個MP4格式的視頻轉換爲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
將兩個音頻進行合併,按照時間較短的音頻時間長度做爲輸出
ffmpeg -i input.wav -filter_complex atempo=0.5 output.wav
將音頻按照0.5倍的速度進行處理生成output.wav 時間長度變爲原來的2倍,音高不變
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
視頻提升亮度 參數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
視頻增長對比度 參數contrast,取值範圍是從-2.0到2.0
ffmpeg -i input.mp4 -vf 「transpose=1」 -b:v 600k output.mp4
視頻旋轉效果器使用
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格式表示的數據轉換爲JPEG格式的圖片
ffmpeg -f rawvideo -pix_fmt yuv420p -s 480*480 -i texture.yuv -f image2 -vcodec mjpeg output.jpg
將一個YUV格式表示的數據轉換爲JPEG格式的圖片
ffmpeg -re -i ipnut.mp4 -acodec copy -vcodec copy -f flv rtmp://xxx
將一段視頻推送到流媒體服務器上
ffmpeg -i http://xxx/xxx.flv -acodec copy -vcodec copy -f flv output.flv
將流媒體服務器上的流dump到本地
額外分享ffprobe和ffplay的一些命令輔助測試.
ffprobe經常使用命令 ffprobe 文件名 查看音頻視頻文件信息 ffprobe -show_format 文件名 查看文件的輸出格式信息,時間長度,文件大小,比特率,流數目等。 ffprobe -print_format json -show_streams 文件名 以json格式輸出詳細信息 ffprobe -show_frames 文件名 顯示幀信息 ffprobe -show_packets 文件名 查看包信息 ffplay經常使用命令 ffplay 文件名 播放音頻、視頻文件 ffplay 文件名 -loop 10 循環播放文件10次 ffplay 文件名 -ast 1 播放視頻第一路音頻流 參數若是是2 就是第二路音頻流 若是沒有就會靜音。 ffplay 文件名 -vst 1 播放視頻第一路視頻流 參數若是是2 就是第二路視頻流 沒有顯示黑屏 ffplay .pcm文件 -f 格式 -channels 2 聲道數 -ar 採樣率 播放pcm文件 必須設置參數正確 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原始數據 ffplay 文件名 -sync audio 指定ffplay使用音頻爲基準進行音視頻同步默認ffplay也是這樣 ffplay 文件名 -sync video 指定ffplay使用視頻爲基準進行音視頻同步 ffplay 文件名 -ext video 指定ffplay使用外部時鐘爲基準進行音視頻同步
ffmpeg官方調用文檔: https://ffmpeg.org/documentation.html,可查看相關參數使用
上面是用命令行方式的調用,須要額外本身編譯ffmpeg對應版本的命令行靜態庫,才能在iOS裏方便調用,除此之外還可使用api方式的代碼調用
在官網上http://ffmpeg.org/documentation.html
能夠找到對應版本的api接口文檔。
再分享一個ffmpeg3.0版本的測試demo 連接: https://pan.baidu.com/s/1RZpiPNy79_zE-gxsQqpryw 提取碼: jfd2 你們能夠直接下載使用,測試學習相關ffmpeg命令!
6.推流
可用這個連接: https://blog.csdn.net/lg767201403/article/details/83009938 測試下視頻推流。