1. 程式人生 > >線上學習解決方案

線上學習解決方案

視訊點播解決方案
流媒體的概念:
	將視訊檔案分成許多小的視訊檔案片段,當用戶請求視訊資源的時候,將視訊檔案片段包通過網路傳輸傳送過去,從而實現一邊傳輸資料包一邊播放視訊

流式傳輸:
1.順序流式傳輸
	即順序下載音、視訊檔案,可以實現邊下載邊播放,不過,使用者只能觀看已下載的視訊內容,無法快進到未
下載的視訊部分,順序流式傳輸可以使用Http伺服器來實現,比如Nginx、Apache等。
2.實時流式傳輸
	實時流式傳輸可以解決順序流式傳輸無法快進的問題,它與Http流式傳輸不同,它必須使用流媒體伺服器並
且使用流媒體協議來傳輸視訊,它比Http流式傳輸複雜。常見的實時流式傳輸協議有RTSP、RTMP、RSVP等。

直播使用rtmp協議:使用rtmp協議需要架設媒體伺服器,造價高

HLS協議視訊播放的流程:
	將視訊拆分成若干ts格式的小檔案,通過m3u8格式的索引檔案對這些ts小檔案建立索引。一般
10秒一個ts檔案,播放器連線m3u8檔案播放,當快進時通過m3u8即可找到對應的索引檔案,並去下載對應的ts文
件,從而實現快進、快退以近實時 的方式播放視訊。

瀏覽器播放器:
	H5播放器(Video.js),flash播放器

斷點續傳解決方案:
1、上傳前先把檔案分成塊
2、一塊一塊的上傳,上傳中斷後重新上傳,已上傳的分塊則不用再上傳
3、各分塊上傳完成最後合併檔案

使用百度提供的WebUpload實現前端斷點續傳,網址 http://fexteam.gz01.bdysite.com/webuploader/

視訊點播解決方案整體流程:
	1.教學管理系統使用WebUpload前端,採用斷點續傳的方式上傳檔案
	2.伺服器端先得到檔案片段,再合併為一個完整的檔案
	3.伺服器端再使用FFmpeg將這個檔案編碼設定引數並拆分為新的.ts結尾的檔案片段,並在.m3u8中儲存視訊的索引,並將.ts檔案和.m3u8檔案儲存到檔案伺服器,並部署到nginx伺服器下
	4.使用者點播視訊,首先會將.m3u8檔案下載下來,再根據.m3u8檔案中的索引排序下載相應的.ts檔案,從而達到快進快退的效果
	5.瀏覽器播放視訊,預設使用h5播放器,如果不支援h5就換位flash播放器,如果還不支援就提示使用者更換瀏覽器
流媒體的原理程式碼:
//檔案分片
@Test
public void testThunk(){
    //獲取目標檔案
    File targetFile = new File("h:/test.avi");
    if(!targetFile.isFile()){
        throw new RuntimeException();
    }
    //指定分片儲存目錄
    File thunkDir = new File("h:/thunkDir/");
    if(!thunkDir.exists()){
        thunkDir.mkdirs();
    }
//設定分片檔案的大小1M,緩衝區1KB int fileSize = 1 * 1024 *1024; byte[] buff = new byte[1024]; //計算分片的結果個數,向上取整 int count = (int)Math.ceil( targetFile.length() * 1.0 / fileSize ); //讀取目標檔案,"r"代表只讀許可權 RandomAccessFile random_read = new RandomAccessFile(targetFile,"r"); //開始對目標檔案分片處理 int len;
for(int i = 1; i <= count ; i++ ){ File thunkFile = new File(thunkDir,i + ""); thunkFile.createNewFile(); //注意,需要新建一個分片檔案 //輸出流,"rw"代表讀寫許可權 RandomAccessFile random_write = new RandomAccessFile(thunkFile,"rw"); while((len = random_read.read(buff)) != -1){ random_write.write(buff,0,len); if(thunkFile.length() >= fileSize){ break; } } //釋放本次輸出流資源 random_write.close(); } //釋放輸入流 random_read.close(); } //合併檔案 @Test public void testMerge(){ //獲取分片檔案目錄 File thunkDir = new File("h:/thunkDir/"); if(!thunkDir.isDirectory()){ throw new RuntimeException(); } //建立合併檔案 File mergeFile = new File("h:/mergeFile.avi"); if(mergeFile.exists()){ mergeFile.delete(); } mergeFile.createNewFile(); //建立輸出流 RandomAccessFile random_write = new RandomAccessFile(mergeFile,"rw"); byte[] buff = new byte[1024]; int len; //開啟分片檔案目錄 File[] files = thunkDir.listFiles(); //files中的檔案可能是亂序的,重新排序,升序 List<File> fileList = Arrays.asList(files); Collections.sort(fileList,new Comparator<File>() { @Override public int compare(File o1, File o2) { return Integer.parseInt(o1.getName()) - Integer.parseInt(o2.getName()); }} ); for(File file : fileList){ RandomAccessFile random_read = new RandomAccessFile(file,"r"); while((len = random_read.read(buff))!=-1){ random_write.write(buff,0,len); } random_read.close(); } random_write.close(); }
=WebUpload============
before-send-file
before-send-file
	檔案開始上傳前前端請求服務端準備上傳工作
    public ResponseResult register(String fileMd5,
                                   String fileName,
                                   Long fileSize,
                                   String mimetype,
                                   String fileExt);
1.根據fileMd5 + fileExt 找到檔案目錄下是否已經存在了這個檔案
2.根據fileMd5從MongoDB中查詢是否已經存在檔案資訊
3.如果兩者都為true,則認為檔案已經存在,丟擲異常
4.建立儲存資料片段的目錄
before-send
before-send
	上傳分塊前前端請求服務端校驗分塊是否存在。
//上傳資料片段前預檢查
 public CheckChunkResult checkChunk(String fileMd5,
                                       Integer chunk,
                                       Integer chunkSize);
1.根據fileMd5 + chunk從資料片段目錄從判斷檔案是否已經存在
2.如果存在,判斷資料是否完整,不完整就刪除

//開始上傳資料片段
   public ResponseResult uploadChunk(MultipartFile file,
                                      Integer chunk,
                                      String fileMd5);
1.先判斷上傳的檔案片段是否為空,空就拋異常
2.以chunk為檔名,在資料片段目錄下建立檔案
3.通過file.getInputStream()得到輸入流,然後寫入到新建的檔案中
after-send-file
after-send-file	
	在所有分塊上傳完成後觸發,可以請求服務端合併分塊檔案
    public ResponseResult mergeChunk(String fileMd5,
                                     String fileName,
                                     Long fileSize,
                                     String mimetype,
                                     String fileExt);
1.根據fileMd5得到資料片段檔案目錄下的所有的thunk檔案
2.對chunk檔案進行排序,輸出到合併檔案中
3.根據DigestUtils.md5DigestAsHex(new FileInputStream(file))得到上傳檔案的MD5編碼,跟fileMd5比較,true表示合併檔案跟原始檔案一致