更新時(shí)間:2020-10-28 來(lái)源:黑馬程序員 瀏覽量:
1. 視頻點(diǎn)播需求分析
1.1 需求描述
視頻點(diǎn)播需求如下:
1、學(xué)生可以在windows瀏覽器上在線觀看視頻。
2、播放器具有快進(jìn)、快退、暫停等基本功能。
1.2 視頻點(diǎn)播解決方案
1.2.1 流媒體
詳細(xì)參考:https://baike.baidu.com/item/%E6%B5%81%E5%AA%92%E4%BD%93/98740?fr=aladdin
概括理解:流媒體就是將視頻文件分成許多小塊兒,將這些小塊兒作為數(shù)據(jù)包通過(guò)網(wǎng)絡(luò)發(fā)送出去,實(shí)現(xiàn)一邊傳輸視頻 數(shù)據(jù) 包一邊觀看視頻。
流式傳輸
在網(wǎng)絡(luò)上傳輸音、視頻信息有兩個(gè)方式:下載和流式傳輸。
下載:就是把音、視頻文件完全下載到本機(jī)后開(kāi)始播放,它的特點(diǎn)是必須等到視頻文件下載完成方可播放,播放等待時(shí)間較長(zhǎng),無(wú)法去播放還未下載的部分視頻。
流式傳輸:就是客戶端通過(guò)鏈接視頻服務(wù)器實(shí)時(shí)傳輸音、視頻信息,實(shí)現(xiàn)“邊下載邊播放”。
流式傳輸包括如下兩種方式:
1) 順序流式傳輸
即順序下載音、視頻文件,可以實(shí)現(xiàn)邊下載邊播放,不過(guò),用戶只能觀看已下載的視頻內(nèi)容,無(wú)法快進(jìn)到未下載的視頻部分,順序流式傳輸可以使用Http服務(wù)器來(lái)實(shí)現(xiàn),比如Nginx、Apache等。
2)實(shí)時(shí)流式傳輸
實(shí)時(shí)流式傳輸可以解決順序流式傳輸無(wú)法快進(jìn)的問(wèn)題,它與Http流式傳輸不同,它必須使用流媒體服務(wù)器并且使用流媒體協(xié)議來(lái)傳輸視頻,它比Http流式傳輸復(fù)雜。常見(jiàn)的實(shí)時(shí)流式傳輸協(xié)議有RTSP、RTMP、RSVP等。
流媒體系統(tǒng)的概要結(jié)構(gòu)
通過(guò)流媒體系統(tǒng)的概要結(jié)構(gòu)學(xué)習(xí)流媒體系統(tǒng)的基本業(yè)務(wù)流程。
1、將原始的視頻文件通過(guò)編碼器轉(zhuǎn)換為適合網(wǎng)絡(luò)傳輸?shù)牧鞲袷?,編碼后的視頻直接輸送給媒體服務(wù)器。
原始的視頻文件通常是事先錄制好的視頻,比如通過(guò)攝像機(jī)、攝像頭等錄像、錄音設(shè)備采集到的音視頻文件,體積較大,要想在網(wǎng)絡(luò)上傳輸需要經(jīng)過(guò)壓縮處理,即通過(guò)編碼器進(jìn)行編碼 。
2、媒體服務(wù)獲取到編碼好的視頻文件,對(duì)外提供流媒體數(shù)據(jù)傳輸接口,接口協(xié)議包括 :HTTP、RTSP、RTMP等
3、播放器通過(guò)流媒體協(xié)議與媒體服務(wù)器通信,獲取視頻數(shù)據(jù),播放視頻。
1.2.2 點(diǎn)播方案
本項(xiàng)目包括點(diǎn)播和直播兩種方式,我們先調(diào)研點(diǎn)播的方案,如下:
1、 播放器通過(guò) http協(xié)議從http服務(wù)器上下載視頻文件進(jìn)行播放
問(wèn)題:必須等到視頻下載完才可以播放,不支持快進(jìn)到某個(gè)時(shí)間點(diǎn)進(jìn)行播放
2、 播放器通過(guò)rtmp協(xié)議連接媒體服務(wù)器以實(shí)時(shí)流方式播放視頻
使用rtmp協(xié)議需要架設(shè)媒體服務(wù)器,造價(jià)高,對(duì)于直播多采用此方案。
3、 播放器使用HLS協(xié)議連接http服務(wù)器(Nginx、Apache等)實(shí)現(xiàn)近實(shí)時(shí)流方式播放視頻
HLS協(xié)議規(guī)定:基于Http協(xié)議,視頻封裝格式為ts,視頻的編碼格式為H264,音頻編碼格式為MP3、AAC或者AC-3
HLS是什么 ?
HLS的工作方式是:將視頻拆分成若干ts格式的小文件,通過(guò)m3u8格式的索引文件對(duì)這些ts小文件建立索引。一般10秒一個(gè)ts文件,播放器連接m3u8文件播放,當(dāng)快進(jìn)時(shí)通過(guò)m3u8即可找到對(duì)應(yīng)的索引文件,并去下載對(duì)應(yīng)的ts文件,從而實(shí)現(xiàn)快進(jìn)、快退以近實(shí)時(shí) 的方式播放視頻。
IOS、Android設(shè)備、及各大瀏覽器都支持HLS協(xié)議。
詳細(xì)參考:https://baike.baidu.com/item/HLS/8328931?fr=aladdin
采用HLS方案即可實(shí)現(xiàn)邊下載邊播放,可以不用使用rtmp等流媒體協(xié)議,不用構(gòu)建專用的媒體服務(wù)器,節(jié)省成本。
本項(xiàng)目點(diǎn)播方案確定為方案3。
2 FFmpeg 的基本使用
我們將視頻錄制完成后,使用視頻編碼軟件對(duì)視頻進(jìn)行編碼,本項(xiàng)目 使用FFmpeg對(duì)視頻進(jìn)行編碼 。
下載 :ffmpeg-20180227-fa0c9d6-win64-static.zip,并解壓,本教程將ffmpeg解壓到了C:\Java_Soft\xczx\ffmpeg-20180227-fa0c9d6-win64-static下。
FFmpeg被許多開(kāi)源項(xiàng)目采用,QQ影音、暴風(fēng)影音、VLC等。
下載:FFmpeg https://www.ffmpeg.org/download.html#build-windows
將C:\Java_Soft\xczx\ffmpeg-20180227-fa0c9d6-win64-static\bin目錄配置在path環(huán)境變量中。
簡(jiǎn)單的測(cè)試:
將一個(gè).avi文件轉(zhuǎn)成mp4、mp3、gif等。
比如我們將lucene.avi文件轉(zhuǎn)成mp4,運(yùn)行如下命令:
ffmpeg -i lucene.avi lucene.mp4
轉(zhuǎn)成mp3:ffmpeg -i lucene.avi lucene.mp3
轉(zhuǎn)成gif:ffmpeg -i lucene.avi lucene.gif
官方文檔(英文):http://ffmpeg.org/ffmpeg.html
2.1 生成m3u8/ts文件
使用ffmpeg生成 m3u8的步驟如下:
第一步:先將avi視頻轉(zhuǎn)成mp4
ffmpeg.exe -i lucene.avi -c:v libx264 -s 1280x720 -pix_fmt yuv420p -b:a 63k -b:v 753k -r 18 .\lucene.mp4
下面把各參數(shù)意思大概講講,大概了解意思即可,不再此展開(kāi)流媒體專業(yè)知識(shí)的講解。
-c:v 視頻編碼為x264 ,x264編碼是H264的一種開(kāi)源編碼格式。
-s 設(shè)置分辨率
-pix_fmt yuv420p:設(shè)置像素采樣方式,主流的采樣方式有三種,YUV4:4:4,YUV4:2:2,YUV4:2:0,它的作用是根據(jù)采樣方式來(lái)從碼流中還原每個(gè)像素點(diǎn)的YUV(亮度信息與色彩信息)值。
-b 設(shè)置碼率,-b:a和-b:v分別表示音頻的碼率和視頻的碼率,-b表示音頻加視頻的總碼率。碼率對(duì)一個(gè)視頻質(zhì)量有很大的作用。
-r:幀率,表示每秒更新圖像畫面的次數(shù),通常大于24肉眼就沒(méi)有連貫與停頓的感覺(jué)了。
第二步:將mp4生成m3u8
ffmpeg -i lucene.mp4 -hls_time 10 -hls_list_size 0 -hls_segment_filename ./hls/lucene_%05d.ts ./hls/lucene.m3u8
-hls_time 設(shè)置每片的長(zhǎng)度,單位為秒
-hls_list_size n: 保存的分片的數(shù)量,設(shè)置為0表示保存所有分片
-hls_segment_filename :段文件的名稱,%05d表示5位數(shù)字
生成的效果是:將lucene.mp4視頻文件每10秒生成一個(gè)ts文件,最后生成一個(gè)m3u8文件,m3u8文件是ts的索引文件。
使用VLC打開(kāi)m3u8文件,測(cè)試播放效果,VLC 是一款自由、開(kāi)源的跨平臺(tái)多媒體播放器及框架,可播放大多數(shù)多媒體文件,以及 DVD、音頻 CD、VCD 及各類流媒體協(xié)議。(http://www.videolan.org/)
3. 視頻(媒資)處理
開(kāi)發(fā)環(huán)境
1. 創(chuàng)建媒資數(shù)據(jù)庫(kù)
導(dǎo)入shcool.sql
2. 創(chuàng)建媒資服務(wù)工程
基于springboot創(chuàng)建工程
3. 上傳文件
3.1 斷點(diǎn)續(xù)傳解決方案
通常視頻文件都比較大,所以對(duì)于媒資系統(tǒng)上傳文件的需求要滿足大文件的上傳要求。http協(xié)議本身對(duì)上傳文件大小沒(méi)有限制,但是客戶的網(wǎng)絡(luò)環(huán)境質(zhì)量、電腦硬件環(huán)境等參差不齊,如果一個(gè)大文件快上傳完了網(wǎng)斷了,電斷了沒(méi)有上傳完成,需要客戶重新上傳,這是致命的,所以對(duì)于大文件上傳的要求最基本的是斷點(diǎn)續(xù)傳。
什么是斷點(diǎn)續(xù)傳:
引用百度百科:斷點(diǎn)續(xù)傳指的是在下載或上傳時(shí),將下載或上傳任務(wù)(一個(gè)文件或一個(gè)壓縮包)人為的劃分為幾個(gè)部分,每一個(gè)部分采用一個(gè)線程進(jìn)行上傳或下載,如果碰到網(wǎng)絡(luò)故障,可以從已經(jīng)上傳或下載的部分開(kāi)始繼續(xù)上傳下載未完成的部分,而沒(méi)有必要從頭開(kāi)始上傳下載,斷點(diǎn)續(xù)傳可以提高節(jié)省操作時(shí)間,提高用戶體驗(yàn)性。
上傳流程如下:
1、上傳前先把文件分成塊
2、一塊一塊的上傳,上傳中斷后重新上傳,已上傳的分塊則不用再上傳
3、各分塊上傳完成最后合并文件
文件下載則同理。
3.2 文件分塊與合并
為了更好的理解文件分塊上傳的原理,下邊用java代碼測(cè)試文件的分塊與合并。
3.3 文件分塊
文件分塊的流程如下:
1、獲取源文件長(zhǎng)度
2、根據(jù)設(shè)定的分塊文件的大小計(jì)算出塊數(shù)
3、從源文件讀數(shù)據(jù)依次向每一個(gè)塊文件寫數(shù)據(jù)
//測(cè)試文件分塊方法 @Test public void testChunk() throws IOException { File sourceFile = new File("F:/develop/ffmpeg/lucene.mp4"); // File sourceFile = new File("d:/logo.png"); String chunkPath = "F:/develop/ffmpeg/chunk/"; File chunkFolder = new File(chunkPath); if(!chunkFolder.exists()){ chunkFolder.mkdirs(); } //分塊大小 long chunkSize = 1024*1024*1; //分塊數(shù)量 long chunkNum = (long) Math.ceil(sourceFile.length() * 1.0 / chunkSize ); if(chunkNum<=0){ chunkNum = 1; } //緩沖區(qū)大小 byte[] b = new byte[1024]; //使用RandomAccessFile訪問(wèn)文件 RandomAccessFile raf_read = new RandomAccessFile(sourceFile, "r"); //分塊 for(int i=0;i //創(chuàng)建分塊文件 File file = new File(chunkPath+i); boolean newFile = file.createNewFile(); if(newFile){ //向分塊文件中寫數(shù)據(jù) RandomAccessFile raf_write = new RandomAccessFile(file, "rw"); int len = -1; while((len = raf_read.read(b))!=-1){ raf_write.write(b,0,len); if(file.length()>chunkSize){ break; } } raf_write.close(); } } raf_read.close(); }
3.4 文件合并
文件合并流程:
1、找到要合并的文件并按文件合并的先后進(jìn)行排序
2、創(chuàng)建合并文件
3、依次從合并的文件中讀取數(shù)據(jù)向合并文件寫入數(shù)據(jù)
//測(cè)試文件合并方法 @Test public void testMerge() throws IOException { //塊文件目錄 File chunkFolder = new File("F:/develop/ffmpeg/chunk/"); //合并文件 File mergeFile = new File("F:/develop/ffmpeg/lucene1.mp4"); if(mergeFile.exists()){ mergeFile.delete(); } //創(chuàng)建新的合并文件 mergeFile.createNewFile(); //用于寫文件 RandomAccessFile raf_write = new RandomAccessFile(mergeFile, "rw"); //指針指向文件頂端 raf_write.seek(0); //緩沖區(qū) byte[] b = new byte[1024]; //分塊列表 File[] fileArray = chunkFolder.listFiles(); // 轉(zhuǎn)成集合,便于排序 List<File> fileList = new ArrayList<File>(Arrays.asList(fileArray)); // 從小到大排序 Collections.sort(fileList, new Comparator<File>() { @Override public int compare(File o1, File o2) { if (Integer.parseInt(o1.getName()) < Integer.parseInt(o2.getName())) { return -1; } return 1; } }); //合并文件 for(File chunkFile:fileList){ RandomAccessFile raf_read = new RandomAccessFile(chunkFile,"rw"); int len = -1; while((len=raf_read.read(b))!=-1){ raf_write.write(b,0,len); } raf_read.close(); } raf_write.close(); }
4. 前端頁(yè)面
WebUploader介紹
如何在web頁(yè)面實(shí)現(xiàn)斷點(diǎn)續(xù)傳?
常見(jiàn)的方案有:
1、通過(guò)Flash上傳,比如SWFupload、Uploadify。
2、安裝瀏覽器插件,變相的pc客戶端,用的比較少。
3、Html5
隨著html5的流行,本項(xiàng)目采用Html5完成文件分塊上傳。
本項(xiàng)目使用WebUploader完成大文件上傳功能的開(kāi)發(fā),WebUploader官網(wǎng)地址:http://fexteam.gz01.bdysite.com/webuploader/
使用WebUploader上傳流程如下:
鉤子方法
在webuploader中提供很多鉤子方法,下邊列出一些重要的:
本項(xiàng)目使用如下鉤子方法:
1)before-send-file
在開(kāi)始對(duì)文件分塊兒之前調(diào)用,可以做一些上傳文件前的準(zhǔn)備工作,比如檢查文件目錄是否創(chuàng)建完成等。
2)before-send
在上傳文件分塊之前調(diào)用此方法,可以請(qǐng)求服務(wù)端檢查分塊是否存在,如果已存在則此分塊兒不再上傳。
3)after-send-file
在所有分塊上傳完成后觸發(fā),可以請(qǐng)求服務(wù)端合并分塊文件。
注冊(cè)鉤子方法源代碼:
WebUploader.Uploader.register({ "before-send-file":"beforeSendFile", "before-send":"beforeSend", "after-send-file":"afterSendFile" }
構(gòu)建WebUploader
使用webUploader前需要?jiǎng)?chuàng)建webUploader對(duì)象。
指定上傳分塊的地址:/api/media/upload/uploadchunk
// 創(chuàng)建uploader對(duì)象,配置參數(shù) this.uploader = WebUploader.create( { swf:"/static/plugins/webuploader/dist/Uploader.swf",//上傳文件的flash文件,瀏覽器不支持h5時(shí)啟動(dòng)flash server:"/api/media/upload/uploadchunk",//上傳分塊的服務(wù)端地址,注意跨域問(wèn)題 fileVal:"file",//文件上傳域的name pick:"#picker",//指定選擇文件的按鈕容器 auto:false,//手動(dòng)觸發(fā)上傳 disableGlobalDnd:true,//禁掉整個(gè)頁(yè)面的拖拽功能 chunked:true,// 是否分塊上傳 chunkSize:1*1024*1024, // 分塊大?。J(rèn)5M) threads:3, // 開(kāi)啟多個(gè)線程(默認(rèn)3個(gè)) prepareNextFile:true// 允許在文件傳輸時(shí)提前把下一個(gè)文件準(zhǔn)備好 } )
before-send-file
文件開(kāi)始上傳前前端請(qǐng)求服務(wù)端準(zhǔn)備上傳工作。
type:"POST", url:"/api/media/upload/register", data:{ // 文件唯一表示 fileMd5:this.fileMd5, fileName: file.name, fileSize:file.size, mimetype:file.type, fileExt:file.ext }
before-send
上傳分塊前前端請(qǐng)求服務(wù)端校驗(yàn)分塊是否存在。
type:"POST", url:"/api/media/upload/checkchunk", data:{ // 文件唯一表示 fileMd5:this.fileMd5, // 當(dāng)前分塊下標(biāo) chunk:block.chunk, // 當(dāng)前分塊大小 chunkSize:block.end-block.start }
after-send-file
在所有分塊上傳完成后觸發(fā),可以請(qǐng)求服務(wù)端合并分塊文件。
type:"POST", url:"/api/media/upload/mergechunks", data:{ fileMd5:this.fileMd5, fileName: file.name, fileSize:file.size, mimetype:file.type, fileExt:file.ext }
頁(yè)面效果
學(xué)員使用:
直接解壓資料x(chóng)c-ui-pc-teach到webstorm工作目錄
通過(guò)nginx訪問(wèn),在nginx中配置 (解決跨域)
server { listen 82; server_name localhost; #視頻中心 location / { proxy_pass http://127.0.0.1:12000; proxy_set_header Host $http_host; add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Credentials true; add_header Access-Control-Allow-Methods GET; } #媒資管理后臺(tái)跨域 location ^~ /api/media/ { proxy_pass http://127.0.0.1:9000/media/; proxy_set_header Host $http_host; add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Credentials true; add_header Access-Control-Allow-Methods "GET,POST,OPTIONS"; } }
可以訪問(wèn) http://127.0.0.1:12000/#/media/upload 或者 http://localhost:82/#/media/upload/ 查看頁(yè)面效果。
5. 媒資服務(wù)端編寫
服務(wù)端需要實(shí)現(xiàn)如下功能:
1、上傳前檢查上傳環(huán)境
檢查文件是否上傳,已上傳則直接返回。
檢查文件上傳路徑是否存在,不存在則創(chuàng)建。
2、分塊檢查
檢查分塊文件是否上傳,已上傳則返回true。
未上傳則檢查上傳路徑是否存在,不存在則創(chuàng)建。
3、分塊上傳
將分塊文件上傳到指定的路徑。
4、合并分塊
將所有分塊文件合并為一個(gè)文件。
在數(shù)據(jù)庫(kù)記錄文件信息。
由于上傳過(guò)程復(fù)雜,開(kāi)發(fā)時(shí)按業(yè)務(wù)流程分別實(shí)現(xiàn)。
配置
application.yml配置上傳文件的路徑:
xc-service-manage-media: upload-location: C:/school/video/ # 媒資保存路徑 ffmpeg-path: C:/Java_Soft/xczx/ffmpeg-20180227-fa0c9d6-win64-static/bin/ffmpeg.exe # ffmpeg路徑
定義Dao
使用mybatis-plus
@Mapper@Componentpublic interface FileMsgMapper extends BaseMapper<FileMsg> {}
定義controller
@RestController @RequestMapping("/media/upload") public class MediaUploadController { @Autowired MediaUploadService mediaUploadService; /** * 文件上傳前的注冊(cè) */ @PostMapping("/register") public ResponseResult register(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt) { return mediaUploadService.register(fileMd5, fileName, fileSize, mimetype, fileExt); } /** * 檢測(cè)分塊 * * @param fileMd5 * @param chunk * @param chunkSize * @return */ @PostMapping("/checkchunk") public ResponseResult checkchunk(String fileMd5, Integer chunk, Integer chunkSize) { return mediaUploadService.checkchunk(fileMd5, chunk, chunkSize); } /** * 上傳分塊 * * @param file * @param fileMd5 * @param chunk * @return */ @PostMapping("/uploadchunk") public ResponseResult uploadchunk(MultipartFile file, String fileMd5, Integer chunk) { return mediaUploadService.uploadchunk(file, fileMd5, chunk); } /** * 合并分塊 * * @param fileMd5 * @param fileName * @param fileSize * @param mimetype * @param fileExt * @return */ @PostMapping("/mergechunks") public ResponseResult mergechunks(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt) { return mediaUploadService.mergechunks(fileMd5, fileName, fileSize, mimetype, fileExt); } }
定義service (略)由于代碼過(guò)多,請(qǐng)參見(jiàn)源碼。
現(xiàn)在視頻已經(jīng)通過(guò)斷點(diǎn)續(xù)傳的方式,上傳到了我們的媒資服務(wù)器, 接下來(lái)就要使用FFmpeg將視頻轉(zhuǎn)換成流媒體。
6.視頻處理技術(shù)方案
如何通過(guò)程序進(jìn)行視頻處理?
ffmpeg是一個(gè)可行的視頻處理程序,可以通過(guò)Java調(diào)用ffmpeg.exe完成視頻處理。
在java中可以使用Runtime類和Process Builder類兩種方式來(lái)執(zhí)行外部程序,工作中至少掌握一種。
本項(xiàng)目使用Process Builder的方式來(lái)調(diào)用ffmpeg完成視頻處理。
關(guān)于Process Builder的測(cè)試如下 :
@Test public void testProcessBuilder(){ ProcessBuilder processBuilder = new ProcessBuilder(); // processBuilder.command("ping","127.0.0.1"); processBuilder.command("ipconfig"); //將標(biāo)準(zhǔn)輸入流和錯(cuò)誤輸入流合并,通過(guò)標(biāo)準(zhǔn)輸入流讀取信息 processBuilder.redirectErrorStream(true); try { //啟動(dòng)進(jìn)程 Process start = processBuilder.start(); //獲取輸入流 InputStream inputStream = start.getInputStream(); //轉(zhuǎn)成字符輸入流 InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"gbk"); int len = -1; char[] c = new char[1024]; StringBuffer outputString = new StringBuffer(); //讀取進(jìn)程輸入流中的內(nèi)容 while ((len= inputStreamReader.read(c))!=-1) { String s = new String(c,0,len); outputString.append(s); System.out.print(s); } inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } @Test public void testFFmpeg(){ ProcessBuilder processBuilder = new ProcessBuilder(); //定義命令內(nèi)容 List<String> command = new ArrayList<>(); command.add("D:\\Program Files\\ffmpeg-20180227-fa0c9d6-win64-static\\bin\\ffmpeg.exe"); command.add("-i"); command.add("E:\\ffmpeg_test\\1.avi"); command.add("-y");//覆蓋輸出文件 command.add("-c:v"); command.add("libx264"); command.add("-s"); command.add("1280x720"); command.add("-pix_fmt"); command.add("yuv420p"); command.add("-b:a"); command.add("63k"); command.add("-b:v"); command.add("753k"); command.add("-r"); command.add("18"); command.add("E:\\ffmpeg_test\\1.mp4"); processBuilder.command(command); //將標(biāo)準(zhǔn)輸入流和錯(cuò)誤輸入流合并,通過(guò)標(biāo)準(zhǔn)輸入流讀取信息 processBuilder.redirectErrorStream(true); try { //啟動(dòng)進(jìn)程 Process start = processBuilder.start(); //獲取輸入流 InputStream inputStream = start.getInputStream(); //轉(zhuǎn)成字符輸入流 InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"gbk"); int len = -1; char[] c = new char[1024]; StringBuffer outputString = new StringBuffer(); //讀取進(jìn)程輸入流中的內(nèi)容 while ((len= inputStreamReader.read(c))!=-1) { String s = new String(c,0,len); outputString.append(s); System.out.print(s); } inputStream.close(); } catch (IOException e) { e.printStackTrace(); } }
我們已經(jīng)準(zhǔn)備好了工具類:
Mp4VideoUtil.java完成avi轉(zhuǎn)mp4
HlsVideoUtil.java完成mp4轉(zhuǎn)hls
可以直接使用。
6.1 處理流程
當(dāng)視頻合并成功后
把視頻處理成m3u8流媒體
把視頻信息寫到數(shù)據(jù)庫(kù)
具體代碼: 在合并完成方法中調(diào)用
@Value("${xc-service-manage-media.ffmpeg-path}") String ffmpeg_path; private void ChangeHLS(File mergeFile, String fileMd5) { // mp4 文件保存目錄 String fileFolderPath = getFileFolderPath(fileMd5); //生成的mp4的文件名稱 String mp4_name = fileMd5 + ".mp4"; //生成的mp4所在的路徑 String mp4folder_path = fileFolderPath; //創(chuàng)建工具類對(duì)象 Mp4VideoUtil mp4VideoUtil = new Mp4VideoUtil(ffmpeg_path, mergeFile.getAbsolutePath(), mp4_name, mp4folder_path); //進(jìn)行處理 String result = mp4VideoUtil.generateMp4(); //4、將mp4生成m3u8和ts文件 //String ffmpeg_path, String video_path, String m3u8_name,String m3u8folder_path //mp4視頻文件路徑 String mp4_video_path = mp4folder_path + mp4_name; //m3u8_name文件名稱 String m3u8_name = fileMd5 + ".m3u8"; //m3u8文件所在目錄 String m3u8folder_path = fileFolderPath + "hls/"; HlsVideoUtil hlsVideoUtil = new HlsVideoUtil(ffmpeg_path, mp4_video_path, m3u8_name, m3u8folder_path); //生成m3u8和ts文件 String tsResult = hlsVideoUtil.generateM3u8(); //保存fileUrl(此url就是視頻播放的相對(duì)路徑) String filePath = fileFolderPath + "hls/" + m3u8_name; // 將fileUrl 保存到數(shù)據(jù)庫(kù) FileMsg fileMsg = new FileMsg(); fileMsg.setFileId(fileMd5); fileMsg.setFileName(mergeFile.getName()); fileMsg.setFilePath(filePath); fileMsg.setFileUrl(filePath.split("C:/school/video")[1]); fileMsgMapper.insert(fileMsg); //優(yōu)化: 文件處理成功后, 可以刪除mp4文件 }
說(shuō)明:
mp4轉(zhuǎn)成m3u8如何判斷轉(zhuǎn)換成功?
第一、根據(jù)視頻時(shí)長(zhǎng)來(lái)判斷,同mp4轉(zhuǎn)換成功的判斷方法。
第二、最后還要判斷m3u8文件內(nèi)容是否完整。
4 播放器
4.1 技術(shù)選型
視頻編碼后要使用播放器對(duì)其進(jìn)行解碼、播放視頻內(nèi)容。在web應(yīng)用中常用的播放器有flash播放器、H5播放器或?yàn)g覽器插件播放器,其中以flash和H5播放器最常見(jiàn)。
flash播放器:缺點(diǎn)是需要在客戶機(jī)安裝Adobe Flash Player播放器,優(yōu)點(diǎn)是flash播放器已經(jīng)很成熟了,并且瀏覽器對(duì)flash支持也很好。
H5播放器:基于h5自帶video標(biāo)簽進(jìn)行構(gòu)建,優(yōu)點(diǎn)是大部分瀏覽器支持H5,不用再安裝第三方的flash播放器,并且隨著前端技術(shù)的發(fā)展,h5技術(shù)會(huì)越來(lái)越成熟。
本項(xiàng)目采用H5播放器,使用Video.js開(kāi)源播放器。
Video.js是一款基于HTML5世界的網(wǎng)絡(luò)視頻播放器。它支持HTML5和Flash視頻,它支持在臺(tái)式機(jī)和移動(dòng)設(shè)備上播放視頻。這個(gè)項(xiàng)目于2010年中開(kāi)始,目前已在40萬(wàn)網(wǎng)站使用。
官方地址:http://videojs.com/
4.2 下載video.js
Video.js: https://github.com/videojs/video.js
videojs-contrib-hls: https://github.com/videojs/videojs-contrib-hls#installation
(videojs-contrib-hls是播放hls的一個(gè)插件)
使用文檔:http://docs.videojs.com/tutorial-videojs_.html
本教程使用 video.js 6.7.3 版本,videojs-contrib-hls 5.14.1版本
將資料中提供的plugins賦值到xc-ui-pc-video項(xiàng)目目錄下
4.3 搭建媒體服務(wù)器
正常使用video.js播放視頻是通過(guò)一個(gè)網(wǎng)頁(yè),用戶通過(guò)瀏覽器打開(kāi)網(wǎng)頁(yè)去播放視頻,網(wǎng)頁(yè)和視頻都從web服務(wù)器請(qǐng)求。
4.3.1 Nginx媒體服務(wù)器
根據(jù)上邊的流程,我們?cè)诿襟w服務(wù)器上安裝Nginx,并配置如下 :
# 視頻靜態(tài)資源 location / { alias C:/CODE/JAVA/school/xc-ui-pc-video/; index index.html index.htm; } }
4.4 測(cè)試video.js
1.把資料中的video.html 復(fù)制到 C:/CODE/JAVA/school/xc-ui-pc-video/ 如圖:
上面已經(jīng)使用nginx 代理 C:/CODE/JAVA/school/xc-ui-pc-video/ 目錄。 所以可以通過(guò)http請(qǐng)求直接訪問(wèn)video.html頁(yè)面
把src路徑修改為自己的媒資訪問(wèn)路徑
測(cè)試:
用戶打開(kāi)瀏覽器輸入 http://localhost:81/video.html 。即可打開(kāi)視頻播放頁(yè)面,并播放。
猜你喜歡:
Java如何發(fā)送響應(yīng)消息體?
2020-10-27Java培訓(xùn)機(jī)構(gòu)培訓(xùn)費(fèi)用多少錢?靠譜培訓(xùn)機(jī)構(gòu)怎么選?
2020-10-27Java培訓(xùn)班培訓(xùn)費(fèi)用大概要多少錢?看完不上當(dāng)
2020-10-27Java培訓(xùn)機(jī)構(gòu)培訓(xùn)費(fèi)用要多少?好口碑Java培訓(xùn)機(jī)構(gòu)推薦
2020-10-27Mybatis 執(zhí)行SQL命令的完整流程
2020-10-27Java培訓(xùn)班培訓(xùn)費(fèi)用揭秘!拒絕上當(dāng)!
2020-10-23