教育行業(yè)A股IPO第一股(股票代碼 003032)

全國咨詢/投訴熱線:400-618-4000

Java如何實現(xiàn)在線播放功能?

更新時間:2020年10月28日14時18分 來源:傳智播客 瀏覽次數(shù):

1. 視頻點播需求分析

1.1 需求描述

視頻點播需求如下:

1、學生可以在windows瀏覽器上在線觀看視頻。

2、播放器具有快進、快退、暫停等基本功能。

1.2 視頻點播解決方案

1.2.1 流媒體

流媒體

詳細參考:https://baike.baidu.com/item/%E6%B5%81%E5%AA%92%E4%BD%93/98740?fr=aladdin

概括理解:流媒體就是將視頻文件分成許多小塊兒,將這些小塊兒作為數(shù)據(jù)包通過網(wǎng)絡發(fā)送出去,實現(xiàn)一邊傳輸視頻 數(shù)據(jù) 包一邊觀看視頻。

流式傳輸

在網(wǎng)絡上傳輸音、視頻信息有兩個方式:下載和流式傳輸。

下載:就是把音、視頻文件完全下載到本機后開始播放,它的特點是必須等到視頻文件下載完成方可播放,播放等待時間較長,無法去播放還未下載的部分視頻。

流式傳輸:就是客戶端通過鏈接視頻服務器實時傳輸音、視頻信息,實現(xiàn)“邊下載邊播放”。

流式傳輸包括如下兩種方式:

1) 順序流式傳輸

即順序下載音、視頻文件,可以實現(xiàn)邊下載邊播放,不過,用戶只能觀看已下載的視頻內(nèi)容,無法快進到未下載的視頻部分,順序流式傳輸可以使用Http服務器來實現(xiàn),比如Nginx、Apache等。

2)實時流式傳輸

實時流式傳輸可以解決順序流式傳輸無法快進的問題,它與Http流式傳輸不同,它必須使用流媒體服務器并且使用流媒體協(xié)議來傳輸視頻,它比Http流式傳輸復雜。常見的實時流式傳輸協(xié)議有RTSP、RTMP、RSVP等。

流媒體系統(tǒng)的概要結(jié)構(gòu)

通過流媒體系統(tǒng)的概要結(jié)構(gòu)學習流媒體系統(tǒng)的基本業(yè)務流程。

1、將原始的視頻文件通過編碼器轉(zhuǎn)換為適合網(wǎng)絡傳輸?shù)牧鞲袷?,編碼后的視頻直接輸送給媒體服務器。

原始的視頻文件通常是事先錄制好的視頻,比如通過攝像機、攝像頭等錄像、錄音設備采集到的音視頻文件,體積較大,要想在網(wǎng)絡上傳輸需要經(jīng)過壓縮處理,即通過編碼器進行編碼 。

2、媒體服務獲取到編碼好的視頻文件,對外提供流媒體數(shù)據(jù)傳輸接口,接口協(xié)議包括 :HTTP、RTSP、RTMP等

3、播放器通過流媒體協(xié)議與媒體服務器通信,獲取視頻數(shù)據(jù),播放視頻。

1.2.2 點播方案

本項目包括點播和直播兩種方式,我們先調(diào)研點播的方案,如下:

1、 播放器通過 http協(xié)議從http服務器上下載視頻文件進行播放

問題:必須等到視頻下載完才可以播放,不支持快進到某個時間點進行播放

2、 播放器通過rtmp協(xié)議連接媒體服務器以實時流方式播放視頻

使用rtmp協(xié)議需要架設媒體服務器,造價高,對于直播多采用此方案。

3、 播放器使用HLS協(xié)議連接http服務器(Nginx、Apache等)實現(xiàn)近實時流方式播放視頻

HLS協(xié)議規(guī)定:基于Http協(xié)議,視頻封裝格式為ts,視頻的編碼格式為H264,音頻編碼格式為MP3、AAC或者AC-3

HLS是什么 ?

HLS

HLS的工作方式是:將視頻拆分成若干ts格式的小文件,通過m3u8格式的索引文件對這些ts小文件建立索引。一般10秒一個ts文件,播放器連接m3u8文件播放,當快進時通過m3u8即可找到對應的索引文件,并去下載對應的ts文件,從而實現(xiàn)快進、快退以近實時 的方式播放視頻。

IOS、Android設備、及各大瀏覽器都支持HLS協(xié)議。

媒體服務器

詳細參考:https://baike.baidu.com/item/HLS/8328931?fr=aladdin

采用HLS方案即可實現(xiàn)邊下載邊播放,可以不用使用rtmp等流媒體協(xié)議,不用構(gòu)建專用的媒體服務器,節(jié)省成本。

本項目點播方案確定為方案3。

2 FFmpeg 的基本使用

我們將視頻錄制完成后,使用視頻編碼軟件對視頻進行編碼,本項目 使用FFmpeg對視頻進行編碼 。

ffmpeg

下載 :ffmpeg-20180227-fa0c9d6-win64-static.zip,并解壓,本教程將ffmpeg解壓到了C:\Java_Soft\xczx\ffmpeg-20180227-fa0c9d6-win64-static下。

FFmpeg被許多開源項目采用,QQ影音、暴風影音、VLC等。

下載:FFmpeg https://www.ffmpeg.org/download.html#build-windows

將C:\Java_Soft\xczx\ffmpeg-20180227-fa0c9d6-win64-static\bin目錄配置在path環(huán)境變量中。

檢測是否安裝成功


簡單的測試:

將一個.avi文件轉(zhuǎn)成mp4、mp3、gif等。

比如我們將lucene.avi文件轉(zhuǎn)成mp4,運行如下命令:

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ù)意思大概講講,大概了解意思即可,不再此展開流媒體專業(yè)知識的講解。

-c:v 視頻編碼為x264 ,x264編碼是H264的一種開源編碼格式。

-s 設置分辨率

-pix_fmt yuv420p:設置像素采樣方式,主流的采樣方式有三種,YUV4:4:4,YUV4:2:2,YUV4:2:0,它的作用是根據(jù)采樣方式來從碼流中還原每個像素點的YUV(亮度信息與色彩信息)值。

-b 設置碼率,-b:a和-b:v分別表示音頻的碼率和視頻的碼率,-b表示音頻加視頻的總碼率。碼率對一個視頻質(zhì)量有很大的作用。

-r:幀率,表示每秒更新圖像畫面的次數(shù),通常大于24肉眼就沒有連貫與停頓的感覺了。

第二步:將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 設置每片的長度,單位為秒

-hls_list_size n: 保存的分片的數(shù)量,設置為0表示保存所有分片

-hls_segment_filename :段文件的名稱,%05d表示5位數(shù)字

生成的效果是:將lucene.mp4視頻文件每10秒生成一個ts文件,最后生成一個m3u8文件,m3u8文件是ts的索引文件。

使用VLC打開m3u8文件,測試播放效果,VLC 是一款自由、開源的跨平臺多媒體播放器及框架,可播放大多數(shù)多媒體文件,以及 DVD、音頻 CD、VCD 及各類流媒體協(xié)議。(http://www.videolan.org/)

3. 視頻(媒資)處理

開發(fā)環(huán)境

1. 創(chuàng)建媒資數(shù)據(jù)庫

導入shcool.sql

創(chuàng)建媒資數(shù)據(jù)庫

2. 創(chuàng)建媒資服務工程

基于springboot創(chuàng)建工程

創(chuàng)建妹子服務工程


3. 上傳文件

3.1 斷點續(xù)傳解決方案

通常視頻文件都比較大,所以對于媒資系統(tǒng)上傳文件的需求要滿足大文件的上傳要求。http協(xié)議本身對上傳文件大小沒有限制,但是客戶的網(wǎng)絡環(huán)境質(zhì)量、電腦硬件環(huán)境等參差不齊,如果一個大文件快上傳完了網(wǎng)斷了,電斷了沒有上傳完成,需要客戶重新上傳,這是致命的,所以對于大文件上傳的要求最基本的是斷點續(xù)傳。

什么是斷點續(xù)傳:

引用百度百科:斷點續(xù)傳指的是在下載或上傳時,將下載或上傳任務(一個文件或一個壓縮包)人為的劃分為幾個部分,每一個部分采用一個線程進行上傳或下載,如果碰到網(wǎng)絡故障,可以從已經(jīng)上傳或下載的部分開始繼續(xù)上傳下載未完成的部分,而沒有必要從頭開始上傳下載,斷點續(xù)傳可以提高節(jié)省操作時間,提高用戶體驗性。

斷點續(xù)傳

上傳流程如下:

1、上傳前先把文件分成塊

2、一塊一塊的上傳,上傳中斷后重新上傳,已上傳的分塊則不用再上傳

3、各分塊上傳完成最后合并文件

文件下載則同理。

3.2 文件分塊與合并

為了更好的理解文件分塊上傳的原理,下邊用java代碼測試文件的分塊與合并。

3.3 文件分塊

文件分塊的流程如下:

1、獲取源文件長度

2、根據(jù)設定的分塊文件的大小計算出塊數(shù)

3、從源文件讀數(shù)據(jù)依次向每一個塊文件寫數(shù)據(jù)



//測試文件分塊方法
@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 = (longMath.ceil(sourceFile.length() * 1.0 / chunkSize );
    if(chunkNum<=0){
        chunkNum = 1;
    }
    //緩沖區(qū)大小
    byte[] b = new byte[1024];
    //使用RandomAccessFile訪問文件
    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、找到要合并的文件并按文件合并的先后進行排序

2、創(chuàng)建合并文件

3、依次從合并的文件中讀取數(shù)據(jù)向合并文件寫入數(shù)據(jù)



//測試文件合并方法
@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<FilefileList = new ArrayList<File>(Arrays.asList(fileArray));
    // 從小到大排序
    Collections.sort(fileList, new Comparator<File>() {
        @Override
        public int compare(File o1File 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. 前端頁面

WebUploader介紹

如何在web頁面實現(xiàn)斷點續(xù)傳?

常見的方案有:

1、通過Flash上傳,比如SWFupload、Uploadify。

2、安裝瀏覽器插件,變相的pc客戶端,用的比較少。

3、Html5

隨著html5的流行,本項目采用Html5完成文件分塊上傳。

本項目使用WebUploader完成大文件上傳功能的開發(fā),WebUploader官網(wǎng)地址:http://fexteam.gz01.bdysite.com/webuploader/

web uploader

使用WebUploader上傳流程如下:

WebUploader上傳流程

鉤子方法

在webuploader中提供很多鉤子方法,下邊列出一些重要的:

鉤子方法

本項目使用如下鉤子方法:

1)before-send-file

在開始對文件分塊兒之前調(diào)用,可以做一些上傳文件前的準備工作,比如檢查文件目錄是否創(chuàng)建完成等。

2)before-send

在上傳文件分塊之前調(diào)用此方法,可以請求服務端檢查分塊是否存在,如果已存在則此分塊兒不再上傳。

3)after-send-file

在所有分塊上傳完成后觸發(fā),可以請求服務端合并分塊文件。

注冊鉤子方法源代碼:



WebUploader.Uploader.register({
  "before-send-file":"beforeSendFile",
  "before-send":"beforeSend",
  "after-send-file":"afterSendFile"
}


構(gòu)建WebUploader

使用webUploader前需要創(chuàng)建webUploader對象。

指定上傳分塊的地址:/api/media/upload/uploadchunk



// 創(chuàng)建uploader對象,配置參數(shù)
this.uploader = WebUploader.create(
  {
    swf:"/static/plugins/webuploader/dist/Uploader.swf",//上傳文件的flash文件,瀏覽器不支持h5時啟動flash
    server:"/api/media/upload/uploadchunk",//上傳分塊的服務端地址,注意跨域問題
    fileVal:"file",//文件上傳域的name
    pick:"#picker",//指定選擇文件的按鈕容器
    auto:false,//手動觸發(fā)上傳
    disableGlobalDnd:true,//禁掉整個頁面的拖拽功能
    chunked:true,// 是否分塊上傳
    chunkSize:1*1024*1024// 分塊大?。J5M)
    threads:3// 開啟多個線程(默認3個)
    prepareNextFile:true// 允許在文件傳輸時提前把下一個文件準備好
  }
)


before-send-file

文件開始上傳前前端請求服務端準備上傳工作。



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

上傳分塊前前端請求服務端校驗分塊是否存在。



type:"POST",
url:"/api/media/upload/checkchunk",
data:{
  // 文件唯一表示
  fileMd5:this.fileMd5,
  // 當前分塊下標
  chunk:block.chunk,
  // 當前分塊大小
  chunkSize:block.end-block.start
}


after-send-file

在所有分塊上傳完成后觸發(fā),可以請求服務端合并分塊文件。



type:"POST",
url:"/api/media/upload/mergechunks",
data:{
  fileMd5:this.fileMd5,
  fileName: file.name,
  fileSize:file.size,
  mimetype:file.type,
  fileExt:file.ext
}


頁面效果

頁面效果

學員使用:

直接解壓資料xc-ui-pc-teach到webstorm工作目錄

通過nginx訪問,在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;      
  } 

  #媒資管理后臺跨域     
  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";     
  }          
}

可以訪問 http://127.0.0.1:12000/#/media/upload 或者 http://localhost:82/#/media/upload/ 查看頁面效果。


5. 媒資服務端編寫

服務端需要實現(xiàn)如下功能:

1、上傳前檢查上傳環(huán)境

檢查文件是否上傳,已上傳則直接返回。

檢查文件上傳路徑是否存在,不存在則創(chuàng)建。

2、分塊檢查

檢查分塊文件是否上傳,已上傳則返回true。

未上傳則檢查上傳路徑是否存在,不存在則創(chuàng)建。

3、分塊上傳

將分塊文件上傳到指定的路徑。

4、合并分塊

將所有分塊文件合并為一個文件。

在數(shù)據(jù)庫記錄文件信息。

由于上傳過程復雜,開發(fā)時按業(yè)務流程分別實現(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;

    /**
     * 文件上傳前的注冊
     */

    @PostMapping("/register")
    public ResponseResult register(String fileMd5String fileNameLong fileSizeString mimetypeString fileExt) {

        return mediaUploadService.register(fileMd5, fileName, fileSize, mimetype, fileExt);

    }

    /**
     * 檢測分塊
     *
     * @param fileMd5
     * @param chunk
     * @param chunkSize
     * @return
     */

    @PostMapping("/checkchunk")
    public ResponseResult checkchunk(String fileMd5Integer chunkInteger chunkSize) {
        return mediaUploadService.checkchunk(fileMd5, chunk, chunkSize);
    }

    /**
     * 上傳分塊
     *
     * @param file
     * @param fileMd5
     * @param chunk
     * @return
     */
    @PostMapping("/uploadchunk")
    public ResponseResult uploadchunk(MultipartFile fileString fileMd5Integer chunk) {
        return mediaUploadService.uploadchunk(file, fileMd5, chunk);
    }

    /**
     * 合并分塊
     *
     * @param fileMd5
     * @param fileName
     * @param fileSize
     * @param mimetype
     * @param fileExt
     * @return
     */
    @PostMapping("/mergechunks")
    public ResponseResult mergechunks(String fileMd5String fileNameLong fileSizeString mimetypeString fileExt) {
        return mediaUploadService.mergechunks(fileMd5, fileName, fileSize, mimetype, fileExt);
    }
}

定義service (略)由于代碼過多,請參見源碼。

現(xiàn)在視頻已經(jīng)通過斷點續(xù)傳的方式,上傳到了我們的媒資服務器, 接下來就要使用FFmpeg將視頻轉(zhuǎn)換成流媒體。

6.視頻處理技術方案

如何通過程序進行視頻處理?

ffmpeg是一個可行的視頻處理程序,可以通過Java調(diào)用ffmpeg.exe完成視頻處理。

在java中可以使用Runtime類和Process Builder類兩種方式來執(zhí)行外部程序,工作中至少掌握一種。

本項目使用Process Builder的方式來調(diào)用ffmpeg完成視頻處理。

關于Process Builder的測試如下 :



@Test
    public void testProcessBuilder(){
        ProcessBuilder processBuilder = new ProcessBuilder();
//       processBuilder.command("ping","127.0.0.1");
       processBuilder.command("ipconfig");
        //將標準輸入流和錯誤輸入流合并,通過標準輸入流讀取信息
        processBuilder.redirectErrorStream(true);
        try {
            //啟動進程
            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();
            //讀取進程輸入流中的內(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<Stringcommand = 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);
        //將標準輸入流和錯誤輸入流合并,通過標準輸入流讀取信息
        processBuilder.redirectErrorStream(true);
        try {
            //啟動進程
            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();
            //讀取進程輸入流中的內(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)準備好了工具類:

Mp4VideoUtil.java完成avi轉(zhuǎn)mp4

HlsVideoUtil.java完成mp4轉(zhuǎn)hls

可以直接使用。

6.1 處理流程

當視頻合并成功后

把視頻處理成m3u8流媒體

把視頻信息寫到數(shù)據(jù)庫

具體代碼: 在合并完成方法中調(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)建工具類對象
        Mp4VideoUtil mp4VideoUtil = new Mp4VideoUtil(ffmpeg_path, mergeFile.getAbsolutePath(), mp4_name, mp4folder_path);

        //進行處理
        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就是視頻播放的相對路徑)
        String filePath = fileFolderPath + "hls/" + m3u8_name;


        // 將fileUrl 保存到數(shù)據(jù)庫
        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文件


    }

說明:

mp4轉(zhuǎn)成m3u8如何判斷轉(zhuǎn)換成功?

第一、根據(jù)視頻時長來判斷,同mp4轉(zhuǎn)換成功的判斷方法。

第二、最后還要判斷m3u8文件內(nèi)容是否完整。

4 播放器

4.1 技術選型

視頻編碼后要使用播放器對其進行解碼、播放視頻內(nèi)容。在web應用中常用的播放器有flash播放器、H5播放器或瀏覽器插件播放器,其中以flash和H5播放器最常見。

flash播放器:缺點是需要在客戶機安裝Adobe Flash Player播放器,優(yōu)點是flash播放器已經(jīng)很成熟了,并且瀏覽器對flash支持也很好。

H5播放器:基于h5自帶video標簽進行構(gòu)建,優(yōu)點是大部分瀏覽器支持H5,不用再安裝第三方的flash播放器,并且隨著前端技術的發(fā)展,h5技術會越來越成熟。

本項目采用H5播放器,使用Video.js開源播放器。

Video.js是一款基于HTML5世界的網(wǎng)絡視頻播放器。它支持HTML5和Flash視頻,它支持在臺式機和移動設備上播放視頻。這個項目于2010年中開始,目前已在40萬網(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的一個插件)

使用文檔:http://docs.videojs.com/tutorial-videojs_.html

本教程使用 video.js 6.7.3 版本,videojs-contrib-hls 5.14.1版本

將資料中提供的plugins賦值到xc-ui-pc-video項目目錄下

搭建媒體服務器


4.3 搭建媒體服務器

正常使用video.js播放視頻是通過一個網(wǎng)頁,用戶通過瀏覽器打開網(wǎng)頁去播放視頻,網(wǎng)頁和視頻都從web服務器請求。


4.3.1 Nginx媒體服務器

Nginx媒體服務器

根據(jù)上邊的流程,我們在媒體服務器上安裝Nginx,并配置如下 :



# 視頻靜態(tài)資源
  location / {    
      alias   C:/CODE/JAVA/school/xc-ui-pc-video/;
      index  index.html index.htm;
  }   
}

4.4 測試video.js

1.把資料中的video.html 復制到 C:/CODE/JAVA/school/xc-ui-pc-video/ 如圖:

測試video.js

上面已經(jīng)使用nginx 代理 C:/CODE/JAVA/school/xc-ui-pc-video/ 目錄。 所以可以通過http請求直接訪問video.html頁面

把src路徑修改為自己的媒資訪問路徑

測試:

用戶打開瀏覽器輸入 http://localhost:81/video.html 。即可打開視頻播放頁面,并播放。


猜你喜歡:

單例中懶漢和惡漢模式的區(qū)別

5分鐘了解密碼的加密加鹽處理

0 分享到:
和我們在線交談!