線上學習解決方案
阿新 • • 發佈:2019-01-06
視訊點播解決方案
流媒體的概念: 將視訊檔案分成許多小的視訊檔案片段,當用戶請求視訊資源的時候,將視訊檔案片段包通過網路傳輸傳送過去,從而實現一邊傳輸資料包一邊播放視訊 流式傳輸: 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表示合併檔案跟原始檔案一致